workflow

package
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: May 13, 2026 License: MIT Imports: 42 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CountSkillsPerRegistry

func CountSkillsPerRegistry(repos []string, st *state.State) map[string]int

CountSkillsPerRegistry counts installed skills per registry by inspecting the Sources field of each installed skill.

func CountStatuses

func CountStatuses(statuses []sync.SkillStatus) map[sync.Status]int

func NormalizeExcerptLine

func NormalizeExcerptLine(line string) string

func PrintRegistryJSON

func PrintRegistryJSON(w io.Writer, registries []config.RegistryConfig, st *state.State) error

PrintRegistryJSON writes registry list as JSON to w.

func PromptNameConflictResolution

func PromptNameConflictResolution(conflict sync.NameConflict) (sync.NameConflictResolution, error)

func RegistryGroupFromName

func RegistryGroupFromName(name string) string

func ResolveKitFilter

func ResolveKitFilter(st *state.State) (filter []string, enabled bool)

ResolveKitFilter resolves the kit-scoped skill set for the current working directory. Returns the allowed skill names and whether a project file was found. All errors are non-fatal; a missing or malformed project file returns (nil, false) so callers fall back to global behavior.

func ResolveProjectMCPServers

func ResolveProjectMCPServers() (servers []string, enabled bool)

ResolveProjectMCPServers resolves project- and kit-declared MCP server names for the current project. All errors are non-fatal; a missing or malformed project file returns (nil, false) so callers do not write runtime settings yet.

func Run

func Run(ctx context.Context, steps []Step, bag *Bag) error

Run executes steps sequentially. No retry, no rollback — Scribe operations are idempotent.

func StepAdopt

func StepAdopt(_ context.Context, b *Bag) error

StepAdopt runs skill adoption as a prelude before registry sync. Adoption errors are non-fatal — they are reported through Formatter and sync continues.

func StepCheckConnected

func StepCheckConnected(_ context.Context, b *Bag) error

func StepConnectSyncError

func StepConnectSyncError(ctx context.Context, b *Bag) error

StepConnectSyncError handles sync errors gracefully during connect. In TTY mode, sync failures are warnings; in non-TTY, they're fatal.

func StepDedupCheck

func StepDedupCheck(_ context.Context, b *Bag) error

func StepEnsureScribeAgent

func StepEnsureScribeAgent(_ context.Context, b *Bag) error

func StepFetchManifest

func StepFetchManifest(ctx context.Context, b *Bag) error

func StepFilterRegistries

func StepFilterRegistries(_ context.Context, b *Bag) error

func StepIndexPublicRegistry

func StepIndexPublicRegistry(ctx context.Context, b *Bag) error

func StepInferRegistryType

func StepInferRegistryType(ctx context.Context, b *Bag) error

func StepInstallKits

func StepInstallKits(_ context.Context, b *Bag) error

StepInstallKits materializes registry-published kits into ~/.scribe/kits.

func StepLoadConfig

func StepLoadConfig(ctx context.Context, b *Bag) error

func StepLoadState

func StepLoadState(_ context.Context, b *Bag) error

func StepPrintRegistryList

func StepPrintRegistryList(_ context.Context, b *Bag) error

StepPrintRegistryList computes connected registry data for rendering. For JSON output it writes directly; for styled output it populates Bag fields for the cmd/ layer to render.

func StepProjectClaudeMCPServers

func StepProjectClaudeMCPServers(_ context.Context, b *Bag) error

StepProjectClaudeMCPServers writes project-resolved MCP server approvals into shared project Claude settings. Server definitions remain in .mcp.json.

func StepProjectMCPServers

func StepProjectMCPServers(_ context.Context, b *Bag) error

StepProjectMCPServers writes project-resolved MCP server selections into each active tool's project config. Server definitions remain in .mcp.json for Claude, and are copied from .mcp.json for Codex and Cursor project configs.

func StepProjectSnippets

func StepProjectSnippets(_ context.Context, b *Bag) error

func StepReconcileSystem

func StepReconcileSystem(_ context.Context, b *Bag) error

func StepResolveFormatter

func StepResolveFormatter(ctx context.Context, b *Bag) error

StepResolveFormatter constructs the Formatter once. Idempotent — if bag.Formatter is already set (e.g. by a parent workflow), it skips. Must run after StepFilterRegistries so b.Repos reflects the actual set.

func StepResolveKitFilter

func StepResolveKitFilter(_ context.Context, b *Bag) error

StepResolveKitFilter loads the project's .scribe.yaml and resolves its kit references against the user's kit library, leaving the resolved skill names on b.KitFilter. All errors are non-fatal: a missing or malformed project file leaves b.KitFilter nil so the syncer applies no kit filtering and behaves like legacy global sync.

func StepResolveMCPServers

func StepResolveMCPServers(_ context.Context, b *Bag) error

StepResolveMCPServers loads the project's .scribe.yaml and resolves project MCP server names into read-only workflow state. It does not write any agent runtime settings.

func StepResolveProjectRoot

func StepResolveProjectRoot(_ context.Context, b *Bag) error

func StepResolveTeamShareMode

func StepResolveTeamShareMode(_ context.Context, b *Bag) error

func StepResolveTools

func StepResolveTools(_ context.Context, b *Bag) error

func StepSaveConfig

func StepSaveConfig(_ context.Context, b *Bag) error

func StepSelectSkills

func StepSelectSkills(ctx context.Context, b *Bag) error

StepSelectSkills resolves which skills to install and sets b.SkillFilter.

  • Skill names in b.Args: install exactly those skills.
  • b.InstallAllFlag: install everything available (no filter).
  • Interactive TTY: show a multi-select picker of available skills.
  • Non-TTY, no args, no --all: error with a hint.

func StepSetSingleRepo

func StepSetSingleRepo(_ context.Context, b *Bag) error

StepSetSingleRepo sets Repos to just the newly connected repo for the sync tail. Used by the connect install-all path.

func StepShowAvailableSkills

func StepShowAvailableSkills(_ context.Context, b *Bag) error

StepShowAvailableSkills prints how many skills the registry offers and tells the user how to install them. Used by the plain connect path.

func StepSyncSkills

func StepSyncSkills(ctx context.Context, b *Bag) error

func StepValidateManifest

func StepValidateManifest(_ context.Context, b *Bag) error

func StepWriteListJSON

func StepWriteListJSON(ctx context.Context, b *Bag) error

StepWriteListJSON emits the JSON form of the list command. It mirrors the loader logic the TUI runs (in cmd/) but writes structured output to stdout instead of rendering a TUI.

func TimeAgo

func TimeAgo(t time.Time) string

TimeAgo returns a human-readable relative time string. Returns "never synced" for the zero value.

func UseJSONOutput

func UseJSONOutput(stdin, stdout *os.File, jsonForced bool) bool

func UseJSONOutputForProcess

func UseJSONOutputForProcess(jsonForced bool) bool

Types

type Bag

type Bag struct {
	// Inputs (set by cmd/ before Run)
	Args               []string
	JSONFlag           bool
	RepoFlag           string // --registry filter
	RemoteFlag         bool   // --remote: show available skills from registries
	BrowseFlag         bool   // browse mode: remote catalog UI with install-first actions
	KitBrowseFlag      bool   // browse mode shows registry kits instead of skills
	InitialQuery       string // initial search/filter text for TUI surfaces
	TrustAllFlag       bool   // --trust-all: approve all package commands without prompting
	InstallAllFlag     bool   // --all: install all available skills without prompting
	ForceBudget        bool   // --force: allow projection over agent description-byte budgets
	ForceKits          bool   // --force-kits: overwrite existing kit files from connect/resync
	RefreshKits        bool   // --refresh-kits: opt into kit refresh during registry resync
	AliasName          string // --alias: install incoming skill under another name on projection conflict
	SkillAliases       map[string]string
	PinnedSkillSources map[string]string
	LazyGitHub         bool // skip eager GitHub client/provider setup for local-only flows
	Factory            *app.Factory

	// SkillFilter is populated by StepSelectSkills with the names the user chose.
	// If non-empty, StepSyncSkills passes it to the Syncer so only those skills
	// are installed. Nil means no filter (all eligible skills processed).
	SkillFilter []string

	// KitFilter is populated by StepResolveKitFilter from the project's
	// .scribe.yaml. When non-nil, StepSyncSkills passes it to the Syncer so
	// only kit-resolved skills are projected into the project's tool dirs.
	KitFilter        []string
	KitFilterEnabled bool

	// ProjectMCPServers is populated by StepResolveMCPServers from kit-declared
	// MCP server names. It is read-only workflow state for future projection;
	// no agent settings are written from this value yet.
	ProjectMCPServers        []string
	ProjectMCPServersEnabled bool

	ProjectSnippets []string

	// Populated by steps
	Config        *config.Config
	State         *state.State
	Client        *gh.Client
	Visibility    RepositoryVisibilityClient
	RegistryIndex registryindex.MetadataClient
	Tools         []tools.Tool
	ProjectRoot   string
	TeamShareMode bool
	Repos         []string // filtered registries to process
	Formatter     Formatter
	StateDirty    bool

	// Provider is the skill discovery/fetch backend. Set by StepLoadConfig.
	Provider provider.Provider

	// Connect-specific
	RepoArg   string // resolved owner/repo from args or prompt
	SourceArg source.SourceSpec
	SourceKey string
	SourceID  string

	// FilterRegistries is injected by cmd/ to bridge flag resolution.
	// If nil, defaults to returning all repos.
	FilterRegistries func(flag string, repos []string) ([]string, error)

	// Results populated by steps for cmd/ to render.
	// List command results (JSON path only — TUI loads its own data):
	LocalSkills   []discovery.Skill             // populated when listing local skills
	RegistryDiffs map[string][]sync.SkillStatus // repo → skill statuses (remote list)
	MultiRegistry bool                          // whether multiple registries are shown
	Partial       bool                          // true when a mutating workflow completed with failures

	// Registry list command results:
	RegistryRepos   []string                // connected registries
	RegistryConfigs []config.RegistryConfig // connected registry config rows
	RegistryCounts  map[string]int          // skills per registry

	// Registry-published kits populated by connect/resync.
	Kits          []provider.KitFile
	KitsInstalled []string
	// contains filtered or unexported fields
}

Bag carries all intermediate state across workflow steps. Each step reads/writes only its relevant fields.

func (*Bag) MarkStateDirty

func (b *Bag) MarkStateDirty()

type ConflictMode

type ConflictMode int

ConflictMode describes how name conflicts may be resolved for the current IO shape and output format.

const (
	ConflictModeInteractive ConflictMode = iota
	ConflictModeJSONEnvelope
)

func ConflictModeForProcess

func ConflictModeForProcess(jsonForced bool) ConflictMode

func ResolveConflictMode

func ResolveConflictMode(stdin, stdout *os.File, jsonForced bool) ConflictMode

ResolveConflictMode centralizes the prompt-vs-envelope decision for name conflict handling across sync, install, and add.

type Formatter

type Formatter interface {
	// Sync lifecycle
	OnSyncStart(repoCount int)
	OnRegistryStart(repo string)
	OnSkillResolved(name string, status sync.SkillStatus)
	OnSkillDownloading(name string)
	OnSkillInstalled(name string, updated bool, revision int)
	OnSkillSkipped(name string, status sync.SkillStatus)
	OnSkillSkippedByDenyList(name, registry string)
	OnSkillError(name string, err error)
	OnBudgetWarning(agent, message string)
	OnNameConflictResolved(conflict sync.NameConflict, resolution sync.NameConflictResolution)
	OnSyncComplete(summary sync.SyncCompleteMsg)
	OnReconcileConflict(name string, conflict state.ProjectionConflict)
	OnReconcileComplete(summary sync.ReconcileCompleteMsg)
	OnLegacyFormat(repo string)

	// Connect lifecycle
	OnConnectDuplicate(repo string)
	OnConnectSaved(repo string)
	OnConnectSyncing()
	OnConnectSyncWarning(repo string, err error)
	OnConnectAvailable(repo string, count int)
	OnKitsInstalled(repo string, kitNames []string)
	OnKitInstallWarning(kitName string, err error)
	OnKitConflict(kitName, existingSource string)

	// Package lifecycle
	OnPackageInstallPrompt(name, command, source string)
	OnPackageApproved(name string)
	OnPackageDenied(name string)
	OnPackageSkipped(name, reason string)
	OnPackageInstalling(name string)
	OnPackageInstalled(name string)
	OnPackageUpdating(name string)
	OnPackageUpdated(name string)
	OnPackageError(name string, err error, stderr string)
	OnPackageHashMismatch(name, oldCmd, newCmd, source string)

	// Adoption lifecycle
	OnAdoptionSkipped(reason string)
	OnAdoptionStarted(candidateCount int)
	OnAdopted(name string, targetTools []string)
	OnAdoptionError(name string, err error)
	OnAdoptionConflictsDeferred(names []string)
	OnAdoptionComplete(adopted, skipped, failed int)

	// Flush writes any buffered output (JSON mode). Text mode is a no-op.
	Flush() error
}

Formatter absorbs all output-mode decisions. Once constructed, callers never branch on JSON vs text or single vs multi-registry.

func NewFormatter

func NewFormatter(useJSON bool, multiRegistry bool) Formatter

NewFormatter resolves the useJSON × multiRegistry matrix once and returns the appropriate Formatter implementation.

func NewFormatterForContext

func NewFormatterForContext(ctx context.Context, useJSON bool, multiRegistry bool) Formatter

func NewFormatterWithWriters

func NewFormatterWithWriters(useJSON bool, multiRegistry bool, out, errOut io.Writer) Formatter

NewFormatterWithWriters is like NewFormatter but allows injecting writers for testing.

func NewFormatterWithWritersAndMeta

func NewFormatterWithWritersAndMeta(useJSON bool, multiRegistry bool, out, errOut io.Writer, meta func() envelope.Meta) Formatter

type ListLocalPackageJSON

type ListLocalPackageJSON struct {
	Name        string   `json:"name"`
	Description string   `json:"description,omitempty"`
	Revision    int      `json:"revision,omitempty"`
	Path        string   `json:"path,omitempty"`
	InstallCmd  string   `json:"install_cmd,omitempty"`
	Sources     []string `json:"sources,omitempty"`
}

type ListLocalSkillJSON

type ListLocalSkillJSON struct {
	Name        string   `json:"name"`
	Description string   `json:"description,omitempty"`
	Package     string   `json:"package,omitempty"`
	Revision    int      `json:"revision,omitempty"`
	ContentHash string   `json:"content_hash,omitempty"`
	Targets     []string `json:"targets"`
	Managed     bool     `json:"managed"`
	Origin      string   `json:"origin,omitempty"`
	Path        string   `json:"path,omitempty"`
}

type ListOutput

type ListOutput map[string]any

ListOutput is the legacy payload shape for `scribe list --json`.

func BuildListJSONData

func BuildListJSONData(ctx context.Context, b *Bag) (ListOutput, bool, error)

type ListRegistryJSON

type ListRegistryJSON struct {
	Registry string                `json:"registry"`
	Skills   []ListRemoteSkillJSON `json:"skills"`
}

type ListRemoteSkillJSON

type ListRemoteSkillJSON struct {
	Name       string   `json:"name"`
	Status     string   `json:"status"`
	Version    string   `json:"version,omitempty"`
	LoadoutRef string   `json:"loadout_ref,omitempty"`
	Maintainer string   `json:"maintainer,omitempty"`
	Agents     []string `json:"agents,omitempty"`
}

type ListRow

type ListRow struct {
	Name      string
	Group     string
	Status    sync.Status
	HasStatus bool
	Version   string
	Author    string
	Source    discovery.Source
	Targets   []string
	Local     *discovery.Skill
	Entry     *manifest.Entry
	LatestSHA string
	Excerpt   string
	Managed   bool
	Origin    state.Origin
	Kind      state.Kind
}

ListRow is the UI-agnostic row shape used by list surfaces.

func BuildKitBrowseRows

func BuildKitBrowseRows(ctx context.Context, bag *Bag) ([]ListRow, []string, error)

func BuildLocalRows

func BuildLocalRows(skills []discovery.Skill, st *state.State) []ListRow

func BuildLocalRowsExcluding

func BuildLocalRowsExcluding(skills []discovery.Skill, matched map[string]bool, st *state.State) []ListRow

func BuildRows

func BuildRows(ctx context.Context, bag *Bag) ([]ListRow, []string, error)

type RegistryJSON

type RegistryJSON struct {
	Registry   string `json:"registry"`
	Visibility string `json:"visibility"`
	SkillCount int    `json:"skill_count"`
}

type RegistryListJSON

type RegistryListJSON struct {
	Registries []RegistryJSON `json:"registries"`
	LastSync   *string        `json:"last_sync"`
}

func BuildRegistryListJSON

func BuildRegistryListJSON(registries []config.RegistryConfig, st *state.State) RegistryListJSON

type RepositoryVisibilityClient

type RepositoryVisibilityClient interface {
	RepositoryIsPrivate(ctx context.Context, owner, repo string) (bool, error)
}

type Step

type Step struct {
	Name string
	Fn   func(ctx context.Context, b *Bag) error
}

Step is a named unit of work in a workflow.

func ConnectInstallAllSteps

func ConnectInstallAllSteps() []Step

ConnectInstallAllSteps returns the connect path that immediately installs every discovered skill from the just-connected registry.

func ConnectInstallAllTail

func ConnectInstallAllTail() []Step

ConnectInstallAllTail returns the connect + install-all path starting at ResolveFormatter, for callers that already loaded config/client state.

func ConnectSteps

func ConnectSteps() []Step

ConnectSteps returns the step list for the connect command. It saves the registry config and shows available skills — it does NOT auto-install anything. Users install skills explicitly with `scribe add`.

func InstallSteps

func InstallSteps() []Step

InstallSteps returns the step list for the install command.

func ListJSONSteps

func ListJSONSteps() []Step

ListJSONSteps returns the step list for `scribe list --json` (or any non-TTY invocation): it loads, branches local-vs-remote, and writes JSON to stdout. Used only for the machine-readable output path.

func ListLoadStepsLocal

func ListLoadStepsLocal() []Step

ListLoadStepsLocal returns the minimal local-only step list needed before launching the list TUI.

func ListLoadStepsRemote

func ListLoadStepsRemote() []Step

ListLoadStepsRemote returns the remote list setup path, including tool resolution for in-place actions on remote rows.

func RegistryListSteps

func RegistryListSteps() []Step

RegistryListSteps returns the step list for the registry list command.

func SyncSteps

func SyncSteps() []Step

SyncSteps returns the step list for the sync command.

func SyncTail

func SyncTail() []Step

SyncTail returns the shared tail of steps reused by connect and create-registry.

Jump to

Keyboard shortcuts

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