Help us shape the future of Go! Take the 2026 Go Developer Survey

install

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package install is the agent-neutral configuration engine behind `graphmaster install` / `uninstall`.

Agent adapters describe the edits they want as a Plan: a list of FileChange values, each targeting one file in one Format:

  • FormatJSON: JSON or JSONC edited through github.com/tailscale/hujson so comments, ordering and unrelated keys survive. Ops: JSONSet (object member at a JSON pointer) and JSONAppend (array element).
  • FormatTOML: a marker block (# graphmaster:begin v1 ... # graphmaster:end) appended or replaced in place; the whole file is re-parsed with go-toml before anything is written.
  • FormatMarkdown: a marker block (<!-- graphmaster:begin v1 --> ... <!-- graphmaster:end -->).
  • FormatGenerated: a file graphmaster owns entirely (e.g. the OpenCode plugin).

The Engine applies plans, taking a timestamped backup of every file it touches the first time and recording pre/post hashes plus the exact insertions in install-state.json. Install is idempotent. Uninstall restores a file byte-for-byte from its backup when the file still has the post-install hash; otherwise it surgically removes only graphmaster's own insertions. Every operation can run as a dry run that reports unified diffs.

Index

Constants

View Source
const BinaryName = "graphmaster"

BinaryName is the executable name looked up on PATH.

View Source
const MarkerVersion = 1

MarkerVersion is written into every begin marker.

View Source
const StateFileName = "install-state.json"

StateFileName is the manifest written next to graphmaster's config.toml.

Variables

This section is empty.

Functions

func Pointer

func Pointer(tokens ...string) string

Pointer builds an RFC 6901 JSON pointer from unescaped reference tokens, e.g. Pointer("mcpServers", "graphmaster") == "/mcpServers/graphmaster".

func StandardizeJSON

func StandardizeJSON(b []byte) ([]byte, error)

StandardizeJSON converts JSONC (comments, trailing commas) to standard JSON suitable for encoding/json, e.g. for doctor checks.

Types

type Action

type Action string

Action is what an operation did (or would do) to a file.

const (
	ActionNone     Action = "unchanged"
	ActionCreate   Action = "create"
	ActionUpdate   Action = "update"
	ActionRestore  Action = "restore"        // byte-exact restore from backup
	ActionDelete   Action = "delete"         // file graphmaster created removed
	ActionSurgical Action = "remove-entries" // only graphmaster entries removed
	ActionMissing  Action = "missing"        // recorded file no longer exists
)

type BinaryLocator

type BinaryLocator struct {
	Executable   func() (string, error)
	LookPath     func(string) (string, error)
	EvalSymlinks func(string) (string, error)
	// TempDirs are roots whose binaries are considered ephemeral
	// (default: os.TempDir()).
	TempDirs []string
}

BinaryLocator finds a stable absolute path to the graphmaster binary for agent configs. Function fields default to the os/exec/filepath versions.

func (BinaryLocator) Resolve

func (l BinaryLocator) Resolve() (BinaryPath, error)

Resolve picks, in order:

  1. the Homebrew prefix bin symlink when running from a Cellar/Caskroom version directory (survives `brew upgrade`);
  2. a PATH entry resolving to the running executable (keeps symlinks);
  3. the running executable itself, unless it is a temporary build (`go run`, go-build dirs, TempDir), in which case an installed graphmaster on PATH is used or an error is returned.

type BinaryPath

type BinaryPath struct {
	Path   string
	Reason string
}

BinaryPath is a resolved binary location.

func ResolveBinary

func ResolveBinary() (BinaryPath, error)

ResolveBinary resolves the running graphmaster binary with defaults.

type Block

type Block struct {
	// ID distinguishes several blocks in one file; "" is the default block.
	ID   string
	Body string
}

Block is a marker-delimited text block for TOML and Markdown files.

type CheckStatus

type CheckStatus string

CheckStatus is how far a file matches one desired plan edit.

const (
	CheckOK      CheckStatus = "ok"      // exactly what the plan wants
	CheckStale   CheckStatus = "stale"   // a graphmaster entry exists but differs
	CheckMissing CheckStatus = "missing" // no graphmaster entry
	CheckInvalid CheckStatus = "invalid" // the file cannot be read or parsed
)

type Component

type Component string

Component groups plan edits by what they do for the agent.

const (
	ComponentMCP          Component = "mcp"
	ComponentHooks        Component = "hooks"
	ComponentInstructions Component = "instructions"
	ComponentOther        Component = "other"
)

func Classify

func Classify(fc FileChange, op Op) Component

Classify guesses what an edit is for from its target and content; it needs no knowledge of a particular agent.

type Container

type Container struct {
	Agent   string `json:"agent"`
	Pointer string `json:"pointer"`
}

Container is a JSON object or array created by an agent's insertions; it is removed on uninstall once empty.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine applies and reverts plans, keeping install-state.json in sync.

func NewEngine

func NewEngine(env Env) *Engine

NewEngine returns an engine bound to env.

func (*Engine) Env

func (e *Engine) Env() Env

Env returns the engine's environment.

func (*Engine) Install

func (e *Engine) Install(opts Options, plans ...Plan) (*Report, error)

Install brings every file in plans to its desired state. All files are parsed and edited in memory first; any error (corrupt JSON, invalid TOML, conflicting definitions) aborts before a single byte is written. Files an agent touched in an earlier install but no longer lists are reverted.

func (*Engine) State

func (e *Engine) State() (*State, error)

State loads the current manifest.

func (*Engine) Uninstall

func (e *Engine) Uninstall(opts Options, agents ...string) (*Report, error)

Uninstall reverts the given agents (all recorded agents when none are named). A file still carrying graphmaster's post-install hash is restored byte-for-byte from its backup (or deleted if graphmaster created it); otherwise only graphmaster's own insertions are removed.

type Env

type Env struct {
	// LookupEnv defaults to os.LookupEnv.
	LookupEnv func(string) (string, bool)
	// Now defaults to time.Now; it names backup directories.
	Now func() time.Time
}

Env abstracts the process environment so tests can inject a temp HOME. The zero value uses the real environment and clock.

func EnvWithHome

func EnvWithHome(home string) Env

EnvWithHome returns an Env whose HOME is home and which ignores every other variable (XDG_CONFIG_HOME included). Intended for tests.

func (Env) BackupDir

func (e Env) BackupDir() (string, error)

BackupDir returns the root directory for timestamped backups.

func (Env) ConfigHome

func (e Env) ConfigHome() (string, error)

ConfigHome returns $XDG_CONFIG_HOME when absolute, else $HOME/.config.

func (Env) Home

func (e Env) Home() (string, error)

Home returns $HOME, falling back to os.UserHomeDir when unset.

func (Env) StateDir

func (e Env) StateDir() (string, error)

StateDir is graphmaster's user config directory (the directory holding config.toml), where install-state.json and backups/ live.

func (Env) StatePath

func (e Env) StatePath() (string, error)

StatePath returns the location of install-state.json.

type FileChange

type FileChange struct {
	// Path is absolute.
	Path   string
	Format Format
	// Ops apply to FormatJSON (JSONSet, JSONAppend) and to FormatTOML /
	// FormatMarkdown (Block).
	Ops []Op
	// Content is the full file for FormatGenerated.
	Content []byte
	// Mode is used when FormatGenerated creates the file (default 0644).
	Mode fs.FileMode
}

FileChange describes the desired graphmaster state of one file.

type FileRecord

type FileRecord struct {
	Path   string `json:"path"`
	Format Format `json:"format"`
	// Existed reports whether the file existed before the first install.
	Existed bool `json:"existed"`
	// PreHash is the sha256 of the file before the first install ("" if absent).
	PreHash string `json:"pre_hash,omitempty"`
	// PostHash is the sha256 after graphmaster's latest write.
	PostHash string `json:"post_hash"`
	// Backup holds the pre-install bytes ("" if the file did not exist).
	Backup string `json:"backup,omitempty"`
	// Restorable is false once the user edited the file between two
	// graphmaster writes: a backup restore would then lose their edit.
	Restorable bool        `json:"restorable"`
	Insertions []Insertion `json:"insertions"`
	Created    []Container `json:"created,omitempty"`
	// CreatedDirs are parent directories graphmaster created, innermost last.
	CreatedDirs []string `json:"created_dirs,omitempty"`
}

FileRecord is the manifest entry for one touched file.

type FileResult

type FileResult struct {
	Path   string
	Agents []string
	Action Action
	// Diff is a unified diff of the change ("" when unchanged).
	Diff string
	Note string
}

FileResult reports one file.

type Format

type Format string

Format selects how a file is edited.

const (
	FormatJSON      Format = "json"      // JSON or JSONC via hujson
	FormatTOML      Format = "toml"      // # graphmaster:begin v1 marker blocks
	FormatMarkdown  Format = "markdown"  // <!-- graphmaster:begin v1 --> blocks
	FormatGenerated Format = "generated" // whole file owned by graphmaster
)

type Insertion

type Insertion struct {
	Agent   string          `json:"agent"`
	Kind    InsertionKind   `json:"kind"`
	Pointer string          `json:"pointer,omitempty"`
	Value   json.RawMessage `json:"value,omitempty"`
	// Prev is the member value graphmaster replaced; restored on removal.
	Prev json.RawMessage `json:"prev,omitempty"`
	// Marker is the block id ("" for the default block).
	Marker string `json:"marker,omitempty"`
	// Sep is the separator inserted before an appended marker block.
	Sep string `json:"sep,omitempty"`
}

Insertion is one exact edit graphmaster made to a file.

type InsertionKind

type InsertionKind string

InsertionKind names what an insertion added.

const (
	KindJSONMember  InsertionKind = "json_member"  // object member at Pointer
	KindJSONElement InsertionKind = "json_element" // array element Value at Pointer
	KindMarker      InsertionKind = "marker"       // marker block Marker
	KindFile        InsertionKind = "file"         // whole generated file
)

type JSONAppend

type JSONAppend struct {
	Pointer string
	Value   any
}

JSONAppend ensures the array at Pointer contains Value, creating the array and missing parents. Elements recorded by an earlier install that are no longer desired are removed first, so changing a hook command replaces the old element instead of accumulating copies.

type JSONSet

type JSONSet struct {
	Pointer string
	// Value is any JSON-marshalable value; json.RawMessage is used verbatim
	// (key order preserved).
	Value any
}

JSONSet makes the object member at Pointer equal Value, creating missing parent objects. A different pre-existing value is replaced and restored on uninstall.

type Op

type Op interface {
	// contains filtered or unexported methods
}

Op is one desired edit; see JSONSet, JSONAppend and Block.

type OpCheck

type OpCheck struct {
	Path      string
	Format    Format
	Op        Op
	Component Component
	Status    CheckStatus
	// Found is graphmaster's current text in the file: the JSON member, the
	// array elements recognized as graphmaster's, the marker block or the
	// whole generated file.
	Found []string
	// Index is the array position of the matching (or stale) element for
	// JSONAppend; -1 otherwise.
	Index int
	// Binaries are the absolute graphmaster executable paths Found refers to.
	Binaries []string
	// Err explains CheckInvalid.
	Err error
}

OpCheck is the read-only verdict for one op of a plan (or one generated file, whose Op is nil).

func CheckPlan

func CheckPlan(p Plan, st *State) []OpCheck

CheckPlan compares p with the files on disk without modifying anything. st (may be nil) identifies array elements graphmaster wrote earlier, so a hook installed with another binary path is reported stale, not missing; without it, elements mentioning graphmaster are recognized by content.

type Options

type Options struct {
	// DryRun computes the report (with diffs) without writing anything.
	DryRun bool
}

Options tunes Install and Uninstall.

type Plan

type Plan struct {
	// Agent identifies the adapter ("claude", "codex", ...). Insertions are
	// recorded per agent so agents can be uninstalled independently.
	Agent string
	Files []FileChange
}

Plan is the set of edits one agent adapter wants.

func (Plan) Validate

func (p Plan) Validate() error

Validate checks the plan's shape without touching the filesystem.

type Report

type Report struct {
	DryRun bool
	Files  []FileResult
}

Report is the outcome of Install or Uninstall.

func (*Report) Changed

func (r *Report) Changed() bool

Changed reports whether any file was (or would be) modified.

func (*Report) String

func (r *Report) String() string

String renders a human-readable summary with diffs.

type State

type State struct {
	Version int                    `json:"version"`
	Files   map[string]*FileRecord `json:"files"`
}

State is the install-state.json manifest.

func LoadState

func LoadState(path string) (*State, error)

LoadState reads the manifest at path; a missing file is an empty state.

func (*State) Agents

func (s *State) Agents() []string

Agents returns every agent with at least one recorded insertion, sorted.

func (*State) FilesOf

func (s *State) FilesOf(agent string) []*FileRecord

FilesOf returns the records touched by agent, sorted by path.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL