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 ResolveRepoSpecTool(flagSpecTool string, interactive bool, prompt func() (string, error)) (string, error)
- func ResolveRunner(flagRunner string) (string, error)
- func ResolveSpecTool(flagSpecTool, existing string, interactive bool, ...) (string, error)
- func ResolveTitleConvention(flagConvention string, interactive bool, prompt func() (string, error)) (string, error)
- func SkillPath(home string) string
- func WriteRepoConfig(repoDir, convention, specTool 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 that an agent should proactively ask about — this is the one place the "which fields exist" list needs to stay current. SpecTool is deliberately excluded: it's an optional per-repo override with a global fallback, not something every repo needs a decided answer for.
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 ResolveRepoSpecTool ¶ added in v0.1.4
func ResolveRepoSpecTool(flagSpecTool string, interactive bool, prompt func() (string, error)) (string, error)
ResolveRepoSpecTool picks the per-repo spec tool override `syntropy setup` should write to `.syntropy.yml`. Precedence: --repo-spec-tool flag, then (if interactive) the prompt's answer, then empty — a non-interactive run with no flag makes no claim about an override rather than guessing one, leaving the repo to fall back to the global default spec tool. Mirrors ResolveTitleConvention's persistence path.
prompt is called only when flagSpecTool is empty and interactive is true; it returns the raw 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 ResolveSpecTool ¶ added in v0.1.4
func ResolveSpecTool(flagSpecTool, existing string, interactive bool, prompt func(existing string) (string, error)) (string, error)
ResolveSpecTool picks the default spec tool `everflow setup` should persist. Precedence: --spec-tool 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 spec tool untouched rather than clobbering it back to empty.
prompt is called only when flagSpecTool 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 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 and spec tool override. It's a no-op (returns false, nil) when both convention and specTool are 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"`
// SpecTool is a per-repo override of the global default spec tool
// (config.Config.SpecTool, ADR-0051/increment-1) an agent should route
// spec creation/viewing to for this repo specifically, e.g. "spec-kit".
// Unlike TitleConvention, this is a pure optional override, not a
// field every repo is expected to have an opinion on — it's never
// included in MissingFields, so an agent following the syntropy Skill
// never prompts for it as part of the per-repo setup conversation; a
// repo with no override simply falls back to the global default.
SpecTool string `yaml:"spec_tool,omitempty"`
}
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) EffectiveSpecTool ¶ added in v0.1.4
func (c RepoConfig) EffectiveSpecTool(globalDefault string) string
EffectiveSpecTool returns the spec tool an agent should actually route spec creation/viewing to: this repo's override if one is set, else globalDefault (config.Config.SpecTool, ADR-0051/increment-1), which itself may be empty — meaning fall back to syntropy's own default spec flow. Unlike EffectiveTitleConvention, there's no BlankSentinel case to unwrap: ADR-0099 deliberately gives SpecTool no "asked, declined" state, since an absent override always means exactly one thing (use the global default).
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.