adapter

package
v0.0.13 Latest Latest
Warning

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

Go to latest
Published: May 8, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

internal/adapter/engine.go

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Detect

func Detect(a Adapter) bool

Detect returns true if the adapter is present on this machine. It iterates the adapter's Detect rules and returns true on the first passing rule. A "command" rule passes if the command exits with code 0. A "path" rule passes if the expanded path exists on the filesystem. Returns false if Detect is empty or all rules fail.

func EstimateTokens

func EstimateTokens(s string) int

EstimateTokens returns a rough token estimate: word count × 1.3. Exported for testing.

func ExpandHome

func ExpandHome(path string) (string, error)

ExpandHome replaces a leading ~ with the user's home directory.

func ExportFileAndClip

func ExportFileAndClip(a Adapter, fullCtx string, opts Options) error

ExportFileAndClip writes fullCtx (raw Markdown) to a.ExportPath and copies a trimmed, formatted short summary plus a guidance line to the clipboard. fullCtx must be raw Markdown — FormatOutput is NOT applied to the file, only to the clipboard payload.

func ExportToClipboard

func ExportToClipboard(content, instructions string, dryRun bool) error

ExportToClipboard writes content to the system clipboard. If clipboard access is unavailable (headless, no display), it falls back to printing the content to stdout with usage instructions.

func HookConfigInstalled

func HookConfigInstalled(settingsPath, key, command string) (bool, error)

HookConfigInstalled reports whether the command appears in the settings JSON.

func ReadMCPConfig

func ReadMCPConfig(settingsPath, key string) (map[string]interface{}, error)

ReadMCPConfig reads the mcpServers map from a JSON settings file. Returns an empty map (not an error) if the file does not exist or the key is absent. Returns an error if the file exists but cannot be parsed, or if the key is not a JSON object.

func RemoveHookConfig

func RemoveHookConfig(settingsPath, key, command string, dryRun bool) error

RemoveHookConfig removes a hook command entry from the tool's settings JSON. No-op if the file does not exist or the entry is not present. When dryRun is true, prints what would happen and returns without writing.

func RemoveSkillFile

func RemoveSkillFile(basePath, skillID string, dryRun bool) error

RemoveSkillFile removes a skill file from the adapter's skill directory. Also removes the parent directory if it becomes empty.

func ReplaceFile

func ReplaceFile(filePath, preamble, content string, dryRun bool) error

ReplaceFile writes preamble + "\n" + content to filePath, overwriting any existing content. If preamble is empty, only content is written (no leading newline). Parent directories are created as needed. When dryRun is true the function prints what it would do but writes nothing.

func ReplaceSection

func ReplaceSection(filePath, section, content string, dryRun bool) error

ReplaceSection writes `content` into `filePath` between HTML comment markers for the named section. If the section already exists it is replaced in-place; otherwise it is appended. Parent directories are created as needed. When dryRun is true the function prints what it would do but writes nothing.

func ResolveSelfArgs added in v0.0.13

func ResolveSelfArgs(command string) string

ResolveSelfArgs returns the command (first word only) resolved to an absolute path if it references sap-devs. Used for MCP configs where command and args are separate fields.

func ResolveSelfCommand added in v0.0.13

func ResolveSelfCommand(command string) string

ResolveSelfCommand returns the command string with "sap-devs" replaced by the absolute path of the running binary. This ensures MCP hosts and hook runners can find the binary without relying on shell PATH. If the command does not reference sap-devs, it is returned unchanged.

func SkillFileInstalled

func SkillFileInstalled(basePath, skillID string) bool

SkillFileInstalled reports whether the skill file exists at the expected location.

func WriteHookConfig

func WriteHookConfig(settingsPath, key, command string, dryRun bool) error

WriteHookConfig adds a hook command entry to the tool's settings JSON. key is a dot-separated JSON path, e.g. "hooks.SessionStart". Idempotent: if the command is already present, it is a no-op. When dryRun is true, prints what would happen and returns without writing.

func WriteMCPConfig

func WriteMCPConfig(settingsPath, key string, server content.MCPServer, dryRun bool) error

WriteMCPConfig merges an MCP server entry into the host's settings JSON file. The file is created if it does not exist. Existing keys are preserved. When dryRun is true, prints what would happen and returns without writing.

func WriteSkillFile

func WriteSkillFile(basePath, skillID, content string, dryRun bool) error

WriteSkillFile writes a skill file to the adapter's skill directory. The skill is placed at <basePath>/<skillID>/SKILL.md. Idempotent: overwrites if content differs.

Types

type Adapter

type Adapter struct {
	ID              string       `yaml:"id"`
	Name            string       `yaml:"name"`
	Type            string       `yaml:"type"` // file-inject | clipboard-export | mcp-wire
	Targets         []Target     `yaml:"targets"`
	Format          string       `yaml:"format,omitempty"` // "markdown" (default) | "plain-prose"
	Template        string       `yaml:"template"`
	Instructions    string       `yaml:"instructions"`
	MaxTokens       int          `yaml:"max_tokens,omitempty"`  // 0 = unconstrained
	MaxBytes        int          `yaml:"max_bytes,omitempty"`   // hard byte ceiling; 0 = unconstrained
	Verbosity       string       `yaml:"verbosity,omitempty"`   // "minimal" | "standard" | "full"; default "full"
	ExportPath      string       `yaml:"export_path,omitempty"` // file-export: path to write full context
	MCPConfig       *MCPConfig   `yaml:"mcp_config,omitempty"`
	ExtraMCPConfigs []MCPConfig  `yaml:"extra_mcp_configs,omitempty"`
	HookConfig      *HookConfig  `yaml:"hook_config,omitempty"`
	SkillConfig     *SkillConfig `yaml:"skill_config,omitempty"`
	Detect          []DetectRule `yaml:"detect"`
}

Adapter defines how to inject SAP context into a specific AI tool.

func LoadAdapters

func LoadAdapters(dir string) ([]Adapter, error)

LoadAdapters reads all *.yaml files from dir and returns the parsed adapters. If dir does not exist, returns an empty slice without error.

func (Adapter) AllMCPConfigs added in v0.0.10

func (a Adapter) AllMCPConfigs() []MCPConfig

AllMCPConfigs returns the primary MCPConfig (if set) plus any extras.

type DetectRule

type DetectRule struct {
	Command string `yaml:"command,omitempty"`
	Path    string `yaml:"path,omitempty"`
}

DetectRule defines a detection method for whether the tool is installed.

type Engine

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

Engine runs injection for a set of adapters, rendering per-adapter with its own budget.

func NewEngine

func NewEngine(adapters []Adapter, packs []*content.Pack, profile *content.Profile, opts Options) *Engine

NewEngine constructs an Engine. A nil Out is normalised to io.Discard.

func (*Engine) RenderSectionContentForTest

func (e *Engine) RenderSectionContentForTest(a Adapter) string

RenderSectionContentForTest exposes renderSectionContent for white-box tests. Do not call this from production code.

func (*Engine) Run

func (e *Engine) Run() RunResult

Run dispatches to the appropriate handler for each adapter.

func (*Engine) Status

func (e *Engine) Status() ([]StatusRow, error)

Status inspects each file-inject adapter target and returns one StatusRow per (adapter, target) pair for the configured scope.

type HookConfig

type HookConfig struct {
	Path   string `yaml:"path"`
	Format string `yaml:"format"` // "json" only for now
	Key    string `yaml:"key"`    // dot-separated JSON path, e.g. "hooks.SessionStart"
}

HookConfig defines where to write hook command entries.

type MCPConfig

type MCPConfig struct {
	Path   string `yaml:"path"`
	Format string `yaml:"format"`
	Key    string `yaml:"key"`
}

MCPConfig defines where to write MCP server configuration.

type Options

type Options struct {
	Scope      string // "global" | "project"
	ToolFilter string // if non-empty, only run this adapter ID
	DryRun     bool
	Stats      bool
	Out        io.Writer               // for stats/warning output; nil → io.Discard
	Dynamic    *content.DynamicContext // nil = no dynamic section
	Uninstall  bool
	// Lang is the active language for i18n. Always use e.opts.Lang inside engine code.
	Lang      string
	Verbosity string // CLI override; empty = use adapter default
}

Options controls inject scope, filtering, dry-run, and stats behaviour.

type RunResult

type RunResult struct {
	Found    int // sections/files removed in live mode
	DryFound int // sections/files that would be removed in dry-run mode
	Err      error
}

RunResult holds the outcome of an Engine.Run() call.

type SectionInfo

type SectionInfo struct {
	Name   string `json:"name"`
	Tokens int    `json:"tokens"`
}

SectionInfo describes a non-sap-devs fenced block found in a target file.

func ScanOtherSections

func ScanOtherSections(content string) []SectionInfo

ScanOtherSections finds non-sap-devs HTML-comment fenced blocks in content. Returns []SectionInfo{} (never nil) so it marshals as [] in JSON. If the same prefix appears multiple times, each start marker produces a separate entry.

type SkillConfig

type SkillConfig struct {
	Path string `yaml:"path"` // base directory, e.g. "~/.claude/skills"
}

SkillConfig defines where to install skill files for this adapter.

type StatusRow

type StatusRow struct {
	AdapterName string `json:"name"`
	AdapterID   string `json:"adapter"`
	Scope       string `json:"scope"`
	TargetPath  string `json:"path"` // unexpanded (~-form)

	FileExists bool `json:"file_exists"`
	Injected   bool `json:"injected"` // sap-devs section present and well-formed
	Orphaned   bool `json:"orphaned"` // markers found but mismatched/reversed

	// Stale is true when the on-disk section content differs from what inject would write today.
	// Always false when FileExists=false, Injected=false, or engine has no packs loaded.
	Stale bool `json:"stale"`

	// Stretch-goal fields — always populated when FileExists=true.
	FileSizeBytes int           `json:"file_size_bytes"`
	FileTokenEst  int           `json:"file_token_est"`  // word count × 1.3
	SapDevsTokens int           `json:"sap_devs_tokens"` // token estimate for sap-devs section only
	OtherSections []SectionInfo `json:"other_sections"`  // non-sap-devs fenced blocks
}

StatusRow is the result of inspecting one adapter target (one row per adapter+target pair). An adapter with both a global and a project target produces two StatusRows.

type Target

type Target struct {
	Scope    string `yaml:"scope"` // global | project
	Path     string `yaml:"path"`
	Mode     string `yaml:"mode"` // replace-section | append | replace-file
	Section  string `yaml:"section"`
	Preamble string `yaml:"preamble,omitempty"` // prepended before content; replace-file only
}

Target is a single file injection target.

Jump to

Keyboard shortcuts

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