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

config

package
v5.6.3 Latest Latest
Warning

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

Go to latest
Published: Oct 24, 2026 License: MIT Imports: 54 Imported by: 0

Documentation

Index

Constants

View Source
const (
	TriggerManual        = "manual"         // Manual activation via @mention (default, no frontmatter needed)
	TriggerAlwaysOn      = "always_on"      // Always active in every interaction
	TriggerModelDecision = "model_decision" // AI decides based on context
	TriggerGlob          = "glob"           // Activate based on file path patterns
)

Trigger constants for the shared `trigger` frontmatter dialect (Devin, Antigravity)

View Source
const (
	ActivationSourceActivation  = "activation"
	ActivationSourceTrigger     = "trigger"
	ActivationSourceAlwaysApply = "alwaysApply"
	ActivationSourceDerived     = "derived"
)

Sources of a resolved activation.

View Source
const (
	// SourceAIRulez is the default source: the .ai-rulez content tree.
	SourceAIRulez = "ai-rulez"
	// SourceAgents reads AGENTS.md and .agents/ as the content tree.
	SourceAgents = "agents"

	// AgentsDirName is the standard directory the "agents" source reads.
	AgentsDirName = ".agents"
	// AgentsConfigFilename is the configuration file kept inside AgentsDirName.
	AgentsConfigFilename = "ai-rulez.toml"
	// AgentsMDFilename is the root instruction file the "agents" source reads.
	AgentsMDFilename = "AGENTS.md"
)

The "agents" source reads the project's knowledge from the open standards instead of from the .ai-rulez content tree: the root AGENTS.md, and the .agents directory (Agent Skills in .agents/skills/<name>/SKILL.md, plus the layout ai-rulez documents for what the standards lack). It is selected with `source = "agents"`, or implied by keeping the configuration itself in .agents/ai-rulez.toml. Output is never written back to what it reads from: AGENTS.md and everything under .agents/ are the source, so the generator leaves them alone.

View Source
const (
	// DiscoverOff serves only the skills ai-rulez defines and its skill sources.
	DiscoverOff = "off"
	// DiscoverRepo also serves every skill directory found in the working tree.
	DiscoverRepo = "repo"
	// DiscoverAll is DiscoverRepo plus the installed and included skills, whatever
	// their delivery.
	DiscoverAll = "all"
)

The modes of [mcp] discover_skills and of `mcp --serve-skills --discover-skills`.

View Source
const (
	// FrontmatterHooks is the normalized name of the hooks key.
	FrontmatterHooks = "hooks"
	// FrontmatterMCPServers is the normalized name of the mcpServers key
	// (also spelled mcp-servers and mcp_servers).
	FrontmatterMCPServers = "mcpservers"
)

Frontmatter keys that make a harness run something or connect to something: the hooks an agent, skill or command declares, and the MCP servers it defines.

View Source
const (
	HeaderNestedCompact = "compact"
	HeaderNestedOmit    = "omit"
	HeaderNestedInherit = "inherit"
)

Header modes accepted by [header] nested.

View Source
const (
	HarnessCopilotCLI  = "copilot-cli"
	HarnessFactory     = "factory"
	HarnessAntigravity = "antigravity"
	HarnessQwen        = "qwen"
	HarnessAugment     = "augment"
	HarnessCodeBuddy   = "codebuddy"
	HarnessQoder       = "qoder"
	HarnessCommandCode = "commandcode"
	HarnessLetta       = "letta"
	HarnessGitLabDuo   = "gitlab-duo"
	HarnessDevin       = "devin"
	HarnessGrok        = "grok"
	HarnessBob         = "bob"
	HarnessCortex      = "cortex"
	HarnessGoose       = "goose"
	HarnessDeepAgents  = "deepagents"
	HarnessJunie       = "junie"
	HarnessZCode       = "zcode"
	HarnessCrush       = "crush"
	HarnessPoolside    = "poolside"
	HarnessReasonix    = "reasonix"
	HarnessHermes      = "hermes"
	HarnessKiro        = "kiro"
	HarnessVibe        = "vibe"
	HarnessKimi        = "kimi"
	HarnessCline       = "cline"
)

Harness names accepted in HookGroup.Targets and HookGroup.Matchers beyond the five of settings_authoring.go. Each is the name of the preset (or provider spec) that renders the harness's native hooks file; see docs/settings.md for the vendor documentation each format was read from.

View Source
const (
	HarnessOpencode = "opencode"
	HarnessKilo     = "kilo"
	HarnessMimocode = "mimocode"
	HarnessPi       = "pi"
	HarnessAmp      = "amp"
)

Harnesses whose hooks are code, not a settings file: each loads a plugin or extension module, so [[hooks]] are rendered into a generated JavaScript or TypeScript file (internal/generator/hookplugins) instead of a document merged into the tool's settings. They are valid in HookGroup.Targets and HookGroup.Matchers like the harnesses of HookHarnesses.

View Source
const (
	// IndexCacheUser keeps the cache in the user cache directory and falls back
	// to a gitignored directory in the project when that is not writable.
	IndexCacheUser = "user"
	// IndexCacheRepo keeps the cache in a gitignored directory in the project.
	IndexCacheRepo = "repo"
	// IndexCacheOff keeps no cache.
	IndexCacheOff = "off"
)

The modes of [mcp] index_cache: where the repository index of `mcp --serve-skills --discover-skills` keeps its cache.

View Source
const (
	PluginFormatClaude       = "claude"
	PluginFormatCodex        = "codex"
	PluginFormatGemini       = "gemini"
	PluginFormatCursor       = "cursor"
	PluginFormatAgentPlugins = "agent-plugins"
)

Plugin layouts an installed_plugins entry can force with `format`.

View Source
const (
	LayerOriginDefault = "default" // named in default_layers of config.toml
	LayerOriginEnv     = "env"     // AI_RULEZ_LAYERS or AI_RULEZ_PROFILE
	LayerOriginOption  = "option"  // the WithLayers load option
)

Layer origins: how a layer became active.

View Source
const (
	OKFIndexStyleBody        = "body"
	OKFIndexStyleFrontmatter = "frontmatter"
)

OKF index styles accepted in okf.index_style.

View Source
const (
	PluginRuntimeClaude       = "claude"
	PluginRuntimeCursor       = "cursor"
	PluginRuntimeCodex        = "codex"
	PluginRuntimeGemini       = "gemini"
	PluginRuntimeKimi         = "kimi"
	PluginRuntimeOpenCode     = "opencode"
	PluginRuntimeFactory      = "factory"
	PluginRuntimeHermes       = "hermes"
	PluginRuntimeAgentPlugins = "agent-plugins"
	PluginRuntimeCopilot      = "copilot"
	PluginRuntimePi           = "pi"
)

PluginRuntime names a supported target runtime for authored plugin bundles.

View Source
const (
	// CodexManifestLegacy writes .codex-plugin/plugin.json only (the default).
	CodexManifestLegacy = "legacy"
	// CodexManifestRoot writes the Agent Plugins root plugin.json that Codex
	// documents as the preferred format, with the Codex interface block under
	// extensions.com.openai, skills/ and a root mcp.json.
	CodexManifestRoot = "root"
	// CodexManifestBoth writes the root manifest and the legacy one.
	CodexManifestBoth = "both"
)

Codex manifest layouts for [plugin.codex] manifest.

View Source
const (
	// PiPackageJSONGenerate writes the root package.json from the plugin
	// metadata, replacing any existing file (the default).
	PiPackageJSONGenerate = "generate"
	// PiPackageJSONMerge updates only the keys ai-rulez owns in an existing root
	// package.json and keeps every other key.
	PiPackageJSONMerge = "merge"
)

Pi package_json modes.

View Source
const (
	// PiSkillsEntryEach lists every skill directory in pi.skills and every file
	// in files (the default).
	PiSkillsEntryEach = "each"
	// PiSkillsEntryDirectory lists the skills directory once in pi.skills and
	// files, so adding a skill does not change package.json.
	PiSkillsEntryDirectory = "directory"
)

Pi skills_entry modes.

View Source
const (
	PlacementCore   = "core"
	PlacementPlugin = "plugin"
)

Placement values for [placement] and the `placement` frontmatter key.

View Source
const (
	// PluginSourceGit is a git URL (https, ssh, git@host:path, file://).
	PluginSourceGit = "git"
	// PluginSourceGitHub is the owner/repo shorthand (or github:owner/repo).
	PluginSourceGitHub = "github"
	// PluginSourceLocal is a directory on disk.
	PluginSourceLocal = "local"
	// PluginSourceIndexURL is an https URL of a marketplace.json file.
	PluginSourceIndexURL = "index-url"
)

Source forms of a marketplace or plugin entry.

View Source
const (
	RoleKindRule    = "rule"
	RoleKindSkill   = "skill"
	RoleKindAgent   = "agent"
	RoleKindCommand = "command"
	RoleKindCheck   = "check"
)

Item kinds roles select on, singular, as used in resolved output and the lock.

View Source
const (
	RoleProblemReference   = "reference-unknown"
	RoleProblemExtends     = "extends-invalid"
	RoleProblemUnreachable = "unreachable-dependency"
)

Problem kinds reported by RoleProblems; the lint package maps them to AR codes.

View Source
const (
	SkillModeFallbackDrop  = "drop"
	SkillModeFallbackServe = "serve"
)

Values of [role_manifest] skill_mode_fallback.

View Source
const (
	// RootConfigFilename is the configuration file of the root layout.
	RootConfigFilename = AgentsConfigFilename
	// RootLocalConfigFilename is its machine-local overlay.
	RootLocalConfigFilename = "ai-rulez.local.toml"
	// RootConfigDirName is the config directory name of the root layout, relative to
	// the project: the project itself.
	RootConfigDirName = "."
)

The root layout keeps the whole configuration in one file, ai-rulez.toml at the project root, with no .ai-rulez/ directory. It is for a repository that hand-writes its own CLAUDE.md and AGENTS.md and only wants ai-rulez for what a config file declares: installed skills, skill sources, includes, MCP servers, hooks, permissions, lint settings and the lock. The configuration is "contentless": the content tree (rules, context, skills, agents, commands, checks, domains) is never scanned, so ConfigDir is the project root and nothing below it is read as authored content.

View Source
const (
	SessionStartWhenRemote = "remote"
	SessionStartWhenAlways = "always"
	SessionStartModeAuto   = "auto"
	SessionStartModeLocked = "locked"
	SessionStartModeFrozen = "frozen"
	SessionStartModePlain  = "plain"
)

Values of [session_start] when and mode.

View Source
const (

	// SessionStartRemoteEnv is the variable Claude Code sets to "true" in a remote (cloud) session.
	SessionStartRemoteEnv = "CLAUDE_CODE_REMOTE"
	// SessionStartDefaultTimeoutSeconds bounds a session-start run unless [session_start] timeout says otherwise.
	SessionStartDefaultTimeoutSeconds = 120

	// SessionStartContextLimit caps the context injected into the agent, in bytes.
	SessionStartContextLimit = 2048
)
View Source
const (
	HarnessClaude  = "claude"
	HarnessCodex   = "codex"
	HarnessCursor  = "cursor"
	HarnessGemini  = "gemini"
	HarnessCopilot = "copilot"
)

Harness names accepted in HookGroup.Targets and HookGroup.Matchers.

View Source
const (
	SkillModeOff               = "off"
	SkillModeUserInvocableOnly = "user-invocable-only"
)

Claude Code skillOverrides states that hide a skill from the model.

View Source
const (
	// ClaudeMDShim keeps the CLAUDE.md "@AGENTS.md" import shim.
	ClaudeMDShim = "shim"
	// ClaudeMDNone omits the shim: the claude preset writes only AGENTS.md,
	// which Claude Code >= 2.1.277 reads directly.
	ClaudeMDNone = "none"
)

ClaudeMD values for the claude_md option. The empty string is the default and behaves like ClaudeMDShim.

View Source
const (
	SkillKindReferences = "references"
	SkillKindScripts    = "scripts"
	SkillKindAssets     = "assets"
)

SkillResourceKind identifiers for the canonical Agent Skills layout.

View Source
const (
	ItemKindSkill   = "skill"
	ItemKindCommand = "command"
)

Author-facing noun for each kind of item that owns a directory. Interpolated into diagnostics, so a command directory is never described as a skill — an author sent to the skills documentation for a commands/ mistake learns nothing.

View Source
const (
	// SkipInstructions skips every root instruction file (CLAUDE.md, AGENTS.md,
	// GEMINI.md, ...), their machine-local variants and the scope roots. It
	// overrides agents_md and claude_md.
	SkipInstructions = "instructions"
	// SkipAgentsMD skips only the shared AGENTS.md (and AGENTS.override.md) and
	// the shared .agents/skills tree.
	SkipAgentsMD = "agents_md"
	// SkipClaudeMD skips only CLAUDE.md.
	SkipClaudeMD = "claude_md"
	// SkipRules skips rule files (.cursor/rules, .claude/rules, ...).
	SkipRules = "rules"
	// SkipSkills skips skill directories.
	SkipSkills = "skills"
	// SkipAgents skips agent definition files.
	SkipAgents = "agents"
	// SkipCommands skips command and prompt files.
	SkipCommands = "commands"
)

Values of the skip_outputs option: output kinds ai-rulez never writes, overwrites or deletes, so a project can hand-write them.

View Source
const (
	SelfLauncherNPX           = "npx"
	SelfLauncherBinaryThenNPX = "binary-then-npx"
)

Values of mcp.self_server_launcher.

View Source
const (
	HeaderHashesFull    = "full"
	HeaderHashesContent = "content"
	HeaderHashesNone    = "none"
)

Header hash modes accepted by [header] hashes.

View Source
const (
	RulesModeSplit  = "split"
	RulesModeInline = "inline"
)

Rules output modes. In split mode each rule is written as its own file in the tool's native rules folder; in inline mode rules are embedded in the root file.

View Source
const (
	BazScopedNested = "nested"
	BazScopedRoot   = "root"
)

Values of rules.baz_scoped.

View Source
const (
	BazUnscopedRoot = "root"
	BazUnscopedSkip = "skip"
)

Values of rules.baz_unscoped.

View Source
const (
	BazRestRoot = "root"
	BazRestSkip = "skip"
)

Values of rules.baz_rest.

View Source
const (
	BazDefaultAll  = "all"
	BazDefaultNone = "none"
)

Values of rules.baz_default.

View Source
const (
	TransportStdio = "stdio"
	TransportHTTP  = "http"
	TransportSSE   = "sse"
)

MCPServer represents an MCP (Model Context Protocol) server configuration MCP transport protocol identifiers, used by preset renderers to decide the per-tool config shape (stdio command vs remote URL keys).

View Source
const (
	// TrustError blocks a skill when the security scan finds anything at all
	// (every finding counts as an error). The default for remote sources.
	TrustError = "error"
	// TrustWarn blocks only findings whose own severity is error (secrets,
	// hidden characters, risky shell); the rest are logged.
	TrustWarn = "warn"
)

Skill source trust (scan) levels.

View Source
const (
	ApprovalSelectorRemote = "remote"
	ApprovalSelectorLocal  = "local"
	ApprovalSelectorAll    = "all"
)

Approval selectors besides "kind:<kind>".

View Source
const (
	AssuranceAsserted     = "asserted"
	AssuranceReviewLinked = "review-linked"
	AssuranceSigned       = "signed"
)

Approval assurance levels, weakest first.

View Source
const (
	ImproveIsolationAuto    = "auto"
	ImproveIsolationNone    = "none"
	ImproveIsolationRequire = "require"
)

Values of [improve] isolation: how `improve run` confines the optimizer.

View Source
const (
	ImproveDefaultMinGain         = 0.05
	ImproveDefaultMaxRegressions  = 0
	ImproveDefaultHoldoutFraction = 0.3
	ImproveDefaultMaxSkillGrowth  = 1.25
	ImproveDefaultRuns            = 3
	ImproveDefaultMaxRounds       = 3
	ImproveDefaultMaxHoldoutEvals = 3
)

Defaults of the acceptance gate. A repository [improve] table may only tighten them (ResolveImprove); internal/improve keeps its own copies, which a test holds equal to these.

View Source
const (
	PluginKindSkills   = "skills"
	PluginKindAgents   = "agents"
	PluginKindCommands = "commands"
	PluginKindHooks    = "hooks"
	PluginKindMCP      = "mcp"
	PluginKindRules    = "rules"
)

Plugin component kinds an installed plugin entry can include or exclude.

View Source
const (
	JudgePromptExtend   = "extend"
	JudgePromptOverride = "override"
)

The prompt modes of [judge.prompts.<category>].

View Source
const (
	// LockScopeAll pins every authored item and, with include_outputs, outputs.
	LockScopeAll = "all"
	// LockScopeSkills pins skills (and remote includes/installed skills) only.
	LockScopeSkills = "skills"
)

Lock scopes select which authored items ai-rulez.lock pins.

View Source
const (
	AgeSourceAuto      = "auto"
	AgeSourceForge     = "forge"
	AgeSourceFirstSeen = "first-seen"
	AgeSourceCommit    = "commit"
)

Sources of a tag's release time (see LockConfig.MinReleaseAgeSource).

View Source
const (
	PublishEmitterTemplate     = "template"
	PublishEmitterCursor       = "cursor-team-marketplace"
	PublishEmitterPort         = "port"
	PublishEmitterAWS          = "aws-agent-registry"
	PublishEmitterKiro         = "kiro-steering"
	PublishEmitterAgentPlugins = "agent-plugins"
	PublishEmitterARD          = "ard"
	PublishEmitterMCPCard      = "mcp-server-card"
	PublishAccessRestricted    = "restricted"
	PublishAccessPublic        = "public"
)

Publish emitter names of [[publish.emitters]] and npm access values (docs/publish.md).

View Source
const (
	ReviewContentDescriptions = "descriptions"
	ReviewContentFull         = "full"
)

Values of [review] content: what an item sends to a judge.

View Source
const (
	ReviewOnSecretWithhold = "withhold"
	ReviewOnSecretRedact   = "redact"
)

Values of [review] on_secret: what happens to an item that holds a credential.

View Source
const (
	ReviewGateInfo    = "info"
	ReviewGateWarning = "warning"
	ReviewGateError   = "error"
)

Values of [review.gate] level: the lowest severity ceiling of a dimension whose stable fail verdict fails the gate. A judge never reports above warning, so "error" gates nothing a judge says.

View Source
const (
	// SigningSubjectLock is the lock attestation.
	SigningSubjectLock = "lock"
	// SigningSubjectBundle is a generated plugin bundle's attestation (sign --bundle).
	SigningSubjectBundle = "bundle"
	// SigningSubjectSkill is a publisher's attestation of one skill directory (sign --skill).
	SigningSubjectSkill = "skill"
	// SigningSubjectSBOM is an attestation of an SBOM file (sign --sbom).
	SigningSubjectSBOM = "sbom"
	// SigningSubjectServed gates `mcp --serve-skills` on the signed lock: a served
	// skill must be pinned in a lock whose attestation verifies. It names no
	// trust entries of its own; the lock's signers apply.
	SigningSubjectServed = "served"
	// SigningSubjectApproval is a signed approval (docs/approvals.md); only a
	// [[signing.trust]] entry can name it.
	SigningSubjectApproval = "approval"
	// SigningTLogRequired, SigningTLogOptional and SigningTLogOff are the tlog modes.
	SigningTLogRequired = "required"
	SigningTLogOptional = "optional"
	SigningTLogOff      = "off"
	// SigningAttestationFile is the default sidecar of the lock, next to it.
	SigningAttestationFile = "ai-rulez.lock.sigstore.json"
)

Signing subjects and TLog modes of the [signing] table (docs/signing.md).

View Source
const (
	TelemetryProtocolHTTPJSON     = "http/json"
	TelemetryProtocolHTTPProtobuf = "http/protobuf"
	TelemetryProtocolGRPC         = "grpc"
)

Protocols accepted by [telemetry] otlp_protocol: OTLP/HTTP with a JSON or a protobuf body, and OTLP/gRPC. All three carry the same allowlisted attributes.

View Source
const (
	TelemetryResourceMaxEntries = 8
	TelemetryResourceMaxValue   = 128
)

Limits of [telemetry.resource].

View Source
const (
	VerifierFileExists      = "file_exists"
	VerifierFileAbsent      = "file_absent"
	VerifierGlobCount       = "glob_count"
	VerifierRegex           = "regex"
	VerifierForbid          = "forbid"
	VerifierKeyEquals       = "key_equals"
	VerifierGeneratedInSync = "generated_in_sync"
)

Verifier predicate types of the flat form. The command and llm predicates exist only in the spec form (`[verifiers.require.command]`, `.llm`), where they are gated by `--allow-exec` and `--allow-llm`.

View Source
const ApproversFromCodeowners = "CODEOWNERS"

ApproversFromCodeowners is the approvers_from value that finds the CODEOWNERS file by itself.

View Source
const BuiltinRubricPrefix = "builtin:"

BuiltinRubricPrefix marks a rubric id that names an embedded rubric.

View Source
const ClaudeSkillDescriptionLimit = 1536

ClaudeSkillDescriptionLimit is the documented cap on the combined description text of one Claude Code skill listing entry.

View Source
const CodeFrontmatterMalformed = "AR306"

CodeFrontmatterMalformed is the lint code (AR306) a malformed frontmatter block is reported under by `validate`.

View Source
const ConfigVersionV5 = "5.0"

ConfigVersionV5 is the config format version this release reads. Older formats are converted by `ai-rulez migrate v5`.

View Source
const DefaultCatalogPageSize = 200

DefaultCatalogPageSize is how many items of the catalog overview one page of the static site shows when [catalog] max_items_per_page is not set.

View Source
const DefaultCatalogSkillName = "plugin-catalog"

DefaultCatalogSkillName is the catalog skill name when none is configured.

View Source
const DefaultCodexSkillsDir = ".agents/skills"

DefaultCodexSkillsDir is where Codex discovers repository skills (https://learn.chatgpt.com/docs/build-skills). Releases before 4.24 wrote ".codex/skills", which Codex does not read; codex_skills_dir = ".codex/skills" restores it.

View Source
const DefaultOKFDir = "docs/okf"

DefaultOKFDir is where the okf preset writes its bundle, relative to the project root.

View Source
const DefaultPiSkillsDir = ".pi/skills"

DefaultPiSkillsDir is where the Pi runtime writes bundled skills when [plugin.pi] skills_dir is unset.

View Source
const DefaultReviewFixMaxGrowthPercent = 25

DefaultReviewFixMaxGrowthPercent is how much a proposed fix may grow an item.

View Source
const DefaultReviewGateMaxAgeDays = 90

DefaultReviewGateMaxAgeDays is how old a calibration record may be when neither the rubric nor [review.gate] sets a limit.

View Source
const DefaultReviewRubric = BuiltinRubricPrefix + "skill-quality"

DefaultReviewRubric is the rubric `ai-rulez review` uses when none is set.

View Source
const DefaultSkillsServerName = SelfMCPServerName

DefaultSkillsServerName is the server name the generated self entry uses for `mcp --serve-skills`; the stub names it when no [[mcp_servers]] entry runs that command. It matches SelfMCPServerName, so the stub and the .mcp.json entry agree.

View Source
const DynamicSkillsName = "dynamic-skills"

DynamicSkillsName is the name of the single generated stub skill that tells the agent to call find_skill / load_skill when any skill is served.

View Source
const EvalsDirName = "evals"

EvalsDirName is the name of the project-level eval tree under the config dir.

View Source
const HookBuiltinGuard = "guard"

HookBuiltinGuard marks the HookGroup that generation synthesizes for [guard].

View Source
const HookBuiltinPlugin = "installed-plugin"

HookBuiltinPlugin marks a HookGroup an installed plugin delivered. Like the guard group it is a runtime marker and is never written back to config.toml.

View Source
const HookBuiltinSessionStart = "session-start"

HookBuiltinSessionStart marks the HookGroup that generation synthesizes for [session_start].

View Source
const HookTypeCommand = "command"

HookTypeCommand is the default (and only bundled) hook handler type: the runtime spawns Command, optionally with Args.

View Source
const ImproveMaxSkillGrowth = 2.0

ImproveMaxSkillGrowth is the largest [improve] max_skill_growth accepted.

View Source
const ImproveMinHoldoutCases = 3

ImproveMinHoldoutCases is the fewest held-out cases `improve` ever accepts; min_holdout_cases may raise it.

View Source
const IncludeFormatOKF = "okf"

IncludeFormatOKF reads an include as an OKF bundle (see docs/okf.md).

View Source
const LegacyLayoutMessage = "" /* 132-byte string literal not displayed */

LegacyLayoutMessage is the deprecation notice for a configuration directory that is not an OKF bundle yet.

View Source
const MaxResourceDescriptionRunes = 160

MaxResourceDescriptionRunes bounds a derived resource description. A reference whose first line is a whole document would otherwise be inlined into the rendered SKILL.md index, inflating it past the serving size limit.

View Source
const MergeFormatOwnedHooks = "owned-hooks"

MergeFormatOwnedHooks is the MergeSource.Format of a hooks file ai-rulez writes whole (Copilot's .github/hooks/ai-rulez.json) rather than merges into.

View Source
const MigrateCommandHint = "ai-rulez migrate v5"

MigrateCommandHint is the command every legacy-config error points at.

View Source
const OKFSpecVersion = "0.2"

OKFSpecVersion is the only OKF spec version ai-rulez writes.

View Source
const PresetLLMsTxt = "llms-txt"

PresetLLMsTxt is the opt-in preset that renders the content tree as llms.txt.

View Source
const PresetOKF = "okf"

PresetOKF is the opt-in preset that keeps an OKF bundle in sync.

View Source
const ProfileDomainNegation = "!"

ProfileDomainNegation marks a profile domain entry as an exclusion: "!name" removes name from the domains the profile selects.

View Source
const ProfileDomainWildcard = "*"

ProfileDomainWildcard is the profile domain entry that selects every domain the config knows. It is only meaningful next to the positive entries a profile already has, so it can be narrowed with ProfileDomainNegation exclusions.

View Source
const ProfileSeparator = ","

ProfileSeparator separates the elements of a composed profile value, as in "base,backend".

View Source
const RedactedValue = "<redacted>"

RedactedValue is shown in place of secret values.

View Source
const ReviewProviderDefaultHost = "provider-default"

ReviewProviderDefaultHost is the allowed_hosts entry that names the provider's own endpoint, used when no base_url is configured.

View Source
const SelfMCPServerName = "ai-rulez"

SelfMCPServerName is the key of the ai-rulez entry in generated MCP files.

View Source
const SkillKindEvals = "evals"

SkillKindEvals is the conventional directory for eval cases next to a skill. It is recognized (no warning) but never emitted into a per-tool skill tree, because cases must not cost context; `[plugin] include_evals` opts in to bundling them.

View Source
const UserConfigFileName = "config.toml"

UserConfigFileName is the user config file inside UserConfigDir: the one file that may set the user-scope-only keys of llm and [telemetry] (docs/trust-model.md).

View Source
const VerifiersDirName = "verifiers"

VerifiersDirName is the directory under a configuration directory that holds verifier declaration files (`*.toml`, each with [[verifiers]] tables).

Variables

View Source
var (
	ErrInvalidPreset = errors.New("invalid preset configuration")
	ErrNoContent     = errors.New("no content loaded")
	// ErrRoleReference marks a role that names something that does not exist (AR971),
	// such as a domain; commands exit 2 on it, the code `validate` uses for findings.
	ErrRoleReference = errors.New("role reference does not resolve")
	// ErrPolicyLoosens marks a repository configuration that tries to loosen the
	// organization policy; commands exit 2 on it, the code `validate` uses for findings.
	ErrPolicyLoosens = errors.New("configuration loosens the organization policy")
	// ErrLockViolation marks a remote include or installed skill that does not
	// match ai-rulez.lock. Unlike other resolution failures it is fatal: loading
	// continues with local content only for a flaky remote, never for a lock mismatch.
	ErrLockViolation = errors.New("ai-rulez.lock violation")
	// ErrIncludeUnresolved marks an include that could not be fetched (no network
	// and no cached copy), read or merged. It is fatal: rendering without it
	// would produce outputs the configuration never described. Only --offline
	// downgrades it to a warning.
	ErrIncludeUnresolved = errors.New("include could not be resolved")
	// ErrSkillUnresolved is ErrIncludeUnresolved for an installed skill: one that
	// cannot be fetched, found or scanned fails the load, because generating
	// without it removes its outputs as stale and lets `generate --check` pass.
	ErrSkillUnresolved = errors.New("installed skill could not be resolved")
	// ErrPluginUnresolved is ErrSkillUnresolved for an installed marketplace or plugin.
	ErrPluginUnresolved = errors.New("installed plugin could not be resolved")
	// ErrIncludeOutsideProject marks a local include declared in the committed
	// project config that resolves outside the project. It is fatal like a lock
	// violation: dropping the include with a warning would hide a path a
	// repository should never be able to name.
	ErrIncludeOutsideProject = errors.New("local include is outside the project")
	// ErrOutputRefused marks a generate run that refused to overwrite a file
	// ai-rulez cannot prove it wrote, so a caller can tell "a user file is in the
	// way" from an I/O failure without matching the message.
	ErrOutputRefused = errors.New("refusing to overwrite a file ai-rulez did not write")
)

Configuration errors

AllPluginRuntimes lists the runtimes emitted by default when a plugin does not restrict Runtimes, in a stable order. Agent Plugins, Copilot and Pi are deliberately excluded: they require an explicit runtimes list so adding them never changes existing bundles' output.

ApprovalAssurances lists the levels min_assurance accepts, weakest first.

View Source
var ApprovalKinds = []string{
	"rule", "context", ItemKindSkill, string(OutputKindAgent), ItemKindCommand, "check", "hook", "role", "settings", "verifier", "rubric", "local-include", "local-skill", "local-marketplace", "local-plugin",
	"include", "installed-skill", "source", "served", "mcp_server", "role-output", "marketplace", "installed-plugin",
}

ApprovalKinds are the kinds `kind:<kind>` accepts: the pinned item kinds and the remote entry kinds. "mcp_server" selects the pinned MCP server declarations (the settings item "mcp-servers").

View Source
var CheckSeverities = []string{"low", "medium", "high", "critical"}

CheckSeverities lists the values a check's `severity` frontmatter may take.

View Source
var DefaultBundleExcludes = []string{gitDirName, ".venv*", "venv", "__pycache__", "*.pyc", "node_modules"}

DefaultBundleExcludes are the names that are never bundled with a skill or command, whatever the project's .gitignore says: virtualenvs, bytecode caches, dependency trees and VCS metadata are build artifacts, not skill content. Projects add to the list with the top-level bundle_exclude config key; an entry prefixed with "!" re-includes a path a default excluded (e.g. "!references/venv").

View Source
var DefaultCodexProjectDocMaxBytes = harnesslimits.MustValue("codex.agents_md_chain_bytes")

DefaultCodexProjectDocMaxBytes is Codex's default project_doc_max_bytes: the most combined AGENTS.md content Codex reads (32 KiB).

View Source
var ErrLegacyConfig = errors.New("legacy V2/V3 config is no longer read")

ErrLegacyConfig reports a V2 or V3 configuration file. ai-rulez v5 reads only the directory config .ai-rulez/config.toml, so such a file is named and the caller is pointed at the 4.x migration instead of being told nothing was found.

View Source
var ErrNoConfig = errors.New("no ai-rulez configuration found")

ErrNoConfig marks a project that has no configuration: no config file in the directory searched. The read-only commands (validate, scan, doctor, okf validate) lint such a project through LoadImplicit; generate, clean and lock need a configuration and tell the user to run `ai-rulez init`.

GuardHarnesses are the harnesses whose documented PreToolUse hook blocks a tool call when the hook exits with code 2 and writes the reason to stderr. A harness outside this list is skipped, never approximated.

Evidence, read 2026-10-06 (docs/settings.md [guard] lists the URLs): Claude Code, Codex, Gemini CLI, Cursor, Factory, Copilot and Copilot CLI each document exit code 2 as a block of the pre-tool call.

View Source
var HookEventsEvaluatingIf = []string{
	hookEventPreToolUse,
	"PostToolUse",
	"PostToolUseFailure",
	"PermissionRequest",
	"PermissionDenied",
}

HookEventsEvaluatingIf lists the events on which Claude Code evaluates a handler's `if` rule. The field is tool-scoped, so it only means anything where a tool call is the subject of the event; on every other event a handler carrying `if` never fires at all. Declaring `if` on, say, SessionStart therefore disables the hook rather than conditioning it, which is worth a warning.

View Source
var HookEventsWithoutMatcher = []string{
	"UserPromptSubmit",
	"PostToolBatch",
	"Stop",
	"TeammateIdle",
	"TaskCreated",
	"TaskCompleted",
	"WorktreeCreate",
	"WorktreeRemove",
	"MessageDisplay",
}

HookEventsWithoutMatcher lists the events that carry no matchable subject, so a declared matcher is silently ignored at runtime. Authors get a warning rather than a hook that appears filtered but is not.

View Source
var HookHarnesses = withPluginHarnesses(slices.Concat([]string{HarnessClaude, HarnessCodex, HarnessCursor, HarnessGemini, HarnessCopilot}, extraHookHarnesses))

HookHarnesses lists the harnesses `[[hooks]]` can be rendered for, in a stable order. Every other preset is reported as unsupported.

HookPluginHarnesses lists the plugin-based harnesses, in a stable order.

View Source
var KnownHookEvents = []string{
	"SessionStart",
	"Setup",
	"UserPromptSubmit",
	"UserPromptExpansion",
	hookEventPreToolUse,
	"PermissionRequest",
	"PermissionDenied",
	"PostToolUse",
	"PostToolUseFailure",
	"PostToolBatch",
	"Notification",
	"MessageDisplay",
	"SubagentStart",
	"SubagentStop",
	"TaskCreated",
	"TaskCompleted",
	"Stop",
	"StopFailure",
	"TeammateIdle",
	"InstructionsLoaded",
	"ConfigChange",
	"CwdChanged",
	"DirectoryAdded",
	"FileChanged",
	"WorktreeCreate",
	"WorktreeRemove",
	"PreCompact",
	"PostCompact",
	"PreModelSwitch",
	"PostModelSwitch",
	"Elicitation",
	"ElicitationResult",
	"SessionEnd",
}

KnownHookEvents lists every lifecycle event Claude Code documents. It exists so a typo in an authored event name ("SesionStart") is reported instead of silently producing a hook that never fires. Membership is advisory only — an event outside this list is warned about, never rejected, so a config written against a newer Claude Code keeps working on an older ai-rulez.

KnownPluginRuntimes lists every runtime the generator can emit, including opt-in ones. Used for validation and schema documentation.

PublishEmitterNames lists every emitter a [[publish.emitters]] entry may name.

RoleKinds lists the kinds a role selects, in output order.

View Source
var RulesDirs = [...]string{
	".claude/rules/",
	".cursor/rules/",
	".devin/rules/",
	".clinerules/",
	".continue/rules/",
	".agents/rules/",
	".junie/rules/",
	".github/instructions/",
}

RulesDirs lists the native rules folders ai-rulez writes one file per rule into. Users hand-write their own rule files in the same folders, so the generator treats them as shared: generated files are gitignored per file and an existing unmanaged file is never overwritten.

The list lives in config, which both the generator and the rule-file planner already import, so neither needs to import the other. The folders of the declarative built-in presets (split rules outputs) are added from the specs embedded in internal/generator/providers/builtin.

VerifierTypes lists the accepted predicate types, in documentation order.

Functions

func AllPresetNames

func AllPresetNames() []string

AllPresetNames returns every built-in preset name, sorted. It is derived from the builtInPresets registry so it cannot drift from validation.

The declarative provider presets are read from the specs embedded in internal/generator/providers/builtin, so the result is the same in every program.

func CacheDir

func CacheDir(parts ...string) (string, error)

CacheDir is a directory below the user cache root, ~/.cache/ai-rulez/<parts...>. It is the one place that decides where fetched includes, installed skills, skill sources and remote documents are cached. There is no fallback to the shared temp directory: a cache other users can write to could hold a tree they planted, and an unlocked source trusts its cache. Without a home directory it errors.

func CacheDirIn

func CacheDirIn(env ambient.Env, parts ...string) (string, error)

CacheDirIn is CacheDir with the home directory taken from env (nil: the real one).

func CanonicalProfile

func CanonicalProfile(value string) string

CanonicalProfile normalizes a profile value for display and lookup: whitespace and empty elements are dropped, element order is preserved. A value with no usable elements is returned unchanged so an error message can quote what the user actually typed.

func CanonicalRoleNames added in v5.0.1

func CanonicalRoleNames(names []string) string

CanonicalRoleNames joins role names into the canonical composition name that --role, the lock and the reports use (deduped, order preserved).

func CheckDescription

func CheckDescription(f *ContentFile) string

CheckDescription returns the declared description of a check, or "".

func CheckMetaString

func CheckMetaString(f *ContentFile, key string) string

CheckMetaString reads a string frontmatter key of a content file.

func CheckPolicy

func CheckPolicy(cfg *Config) error

CheckPolicy fails when the installed policy had to clamp cfg: generation, validation and the MCP servers refuse a configuration that tried to loosen the policy. It is nil without a policy or a violation.

In --policy-mode warn it is nil: the violations are PolicyWarnings instead.

func CheckRepoTelemetryEnv

func CheckRepoTelemetryEnv(name, value string) error

CheckRepoTelemetryEnv rejects a repository-declared environment entry (Claude managed settings env, MCP server env) that would widen telemetry. Those entries reach hook and server processes as real environment variables, which outrank user config, so a repository could otherwise enable export and pick a destination. Only entries that turn telemetry off are allowed.

func CheckSeverity

func CheckSeverity(f *ContentFile) string

CheckSeverity returns the declared severity of a check, lowercased, or "".

func CheckVersion

func CheckVersion(v string) error

CheckVersion verifies that v is a config version this release reads. A 4.x version is rejected with an error that names the migration command; a 2.x or 3.x version is older than `migrate v5` reads and must go through ai-rulez 4.x first.

func ClassifyPluginSource added in v5.5.0

func ClassifyPluginSource(source string) string

ClassifyPluginSource says which form a marketplace or plugin source is. A local path starts with ./, ../, / or ~ (or is absolute on Windows); a bare owner/repo is a GitHub repository, as in Claude Code.

func DefaultInstalledSkillResources added in v5.5.0

func DefaultInstalledSkillResources() []string

DefaultInstalledSkillResources returns what an installed skill bundles when its entry sets no `resources`: every file in the skill directory, so a skill that keeps its documents beside SKILL.md or in a folder of its own (rules/, docs/) arrives whole. Eval cases, tool display metadata (agents/) and hidden files stay out, as they do for a skill's canonical directories.

func DescribeLocalOverlay

func DescribeLocalOverlay(baseDir string) (*LocalOverlay, []OverlayChange, error)

DescribeLocalOverlay lists the keys the overlay sets, each with the shared value it replaces. Values of env/headers entries and of secret-looking keys are flagged Redacted; callers must not print them.

func DescribeLocalOverlayAt

func DescribeLocalOverlayAt(configDir string) (*LocalOverlay, []OverlayChange, error)

DescribeLocalOverlayAt is DescribeLocalOverlay for an explicit config directory.

func DiscoverProjectRoot added in v5.5.0

func DiscoverProjectRoot(ctx context.Context, start string) (string, error)

DiscoverProjectRoot returns the nearest directory at or above start that holds a configuration in any layout, stopping at SearchStopDir. A missing configuration is reported as ErrNoConfig.

func DocForJSON

func DocForJSON(doc map[string]any) map[string]any

DocForJSON returns a shallow copy of a merged document with the TOML key "schema" renamed to "$schema", ready to be marshaled to JSON and decoded into Config.

func DomainActive added in v5.0.1

func DomainActive(domain string, content *ContentTree) bool

DomainActive reports whether a resource tagged with domain is active in a content tree. An empty domain is global (always active). A non-empty domain is active only when the (already-selected) tree carries it; a nil tree therefore activates nothing but untagged resources. It is the shared predicate behind every domain-tagged resource kind, so an MCP server, an installed skill and a skill source all resolve their domain the same way.

func ExcludedFromRecursion added in v5.6.3

func ExcludedFromRecursion(rel string, patterns []string) bool

ExcludedFromRecursion reports whether rel, a slash-separated path relative to the directory a recursive run starts from, is or lies below a directory one of patterns names: a path.Match glob such as "svc/*/gen" or a plain directory such as "tests/fixtures".

func FindConfigFile

func FindConfigFile(startDir string) (string, error)

func FindConfigFileInDirName

func FindConfigFileInDirName(startDir, configDirName string) (string, error)

func FindConfigFileUntil added in v5.5.0

func FindConfigFileUntil(startDir, configDirName, stopDir string) (string, error)

FindConfigFileUntil is FindConfigFileInDirName that does not look above stopDir: that directory is the last one searched. An empty stopDir walks to the file system root.

func FindLegacyConfig

func FindLegacyConfig(baseDir string) string

FindLegacyConfig returns the V2/V3 config file that baseDir holds, or "": a config.yaml, config.yml or config.json in .ai-rulez/ (or .config/ai-rulez/), or a flat ai-rulez.yaml beside the project. A directory with a config.toml has none that matters, so it reports "" then.

func FindProjectRoot added in v5.5.0

func FindProjectRoot(startDir, stopDir string) (string, error)

FindProjectRoot returns the nearest directory at or above startDir that holds a configuration in any layout (.agents/ai-rulez.toml, .ai-rulez/config.toml, .config/ai-rulez/config.toml or ai-rulez.toml), never looking above stopDir (empty: the file system root). It is the base directory to load the project from; a missing configuration is reported as ErrNoConfig.

func FrontmatterProblem

func FrontmatterProblem(path string) string

FrontmatterProblem says why the frontmatter of the file at path cannot be used, as "path:line: reason". The loader only records that a block was unusable; this reads the file again to find the line and the parser's own error. It never fails: without a readable reason it still names the file.

func GeneratePresets

func GeneratePresets(cfg *Config) (map[string][]OutputFile, error)

GeneratePresets generates all configured presets for a config

func HarnessSupportsMCP

func HarnessSupportsMCP(preset string) bool

HarnessSupportsMCP reports whether the named preset's harness can call MCP tools, which is what serving a skill needs. Custom and unknown presets report false: they are not known to, so served skills stay static for them.

func InRulesDir

func InRulesDir(relPath string) bool

InRulesDir is In for the built-in rules folders only; use Config.InRulesDir where a project's custom folders matter.

func IndividualPresetNames

func IndividualPresetNames() []string

IndividualPresetNames returns the per-tool presets, excluding the shared `mcp` config preset and the `okf` and `llms-txt` knowledge presets (neither targets a coding tool), sorted.

func InitLocalOverlay

func InitLocalOverlay(baseDir string) (path string, created bool, err error)

InitLocalOverlay creates a commented config.local.* skeleton in the main config's format. created is false when an overlay already exists.

func InitLocalOverlayAt

func InitLocalOverlayAt(configDir string) (path string, created bool, err error)

InitLocalOverlayAt is InitLocalOverlay for an explicit config directory.

func IsAgentsConfigDir added in v5.2.0

func IsAgentsConfigDir(dir string) bool

IsAgentsConfigDir reports whether dir is the .agents directory or its .ai-rulez state directory, the two spellings of the config directory of a project configured in .agents/ai-rulez.toml.

func IsBuiltInPresetName

func IsBuiltInPresetName(name string) bool

IsBuiltInPresetName reports whether name is a built-in preset.

func IsConfigDirAncestor

func IsConfigDirAncestor(path, configDirName string) bool

IsConfigDirAncestor reports whether path is a strict ancestor directory on the way to a possibly nested config directory target — for example ".config" (or "svc/.config") for target ".config/ai-rulez". Recursive walks use it to descend into a wrapper directory that would otherwise be pruned as hidden.

func IsExecutingFrontmatterKey

func IsExecutingFrontmatterKey(key string) bool

IsExecutingFrontmatterKey reports whether key, in any spelling, declares hooks or MCP servers.

func IsGitDirName

func IsGitDirName(seg string) bool

IsGitDirName reports whether one path segment names a git directory under any spelling a filesystem folds onto ".git".

func IsHomeSource added in v5.5.0

func IsHomeSource(source string) bool

IsHomeSource reports whether source is "~" or "~/path".

func IsLegacyLayout

func IsLegacyLayout(configDir string) bool

IsLegacyLayout reports whether configDir still uses the pre-OKF layout: it holds markdown content and has no root index.md. A directory without content has nothing to migrate.

func IsLegacyVersion

func IsLegacyVersion(v string) bool

IsLegacyVersion reports whether v names a config format older than v5 ("2.0", "3.0", "4.0", "4.1", ...).

func IsOKFListing

func IsOKFListing(data []byte) bool

IsOKFListing reports whether data has the shape of a generated OKF index or log (it lists at least one entry): at most an okf_version frontmatter, and a body of headings, list entries and blank lines.

func IsOKFReservedKey

func IsOKFReservedKey(key string) bool

IsOKFReservedKey reports whether a top-level frontmatter key belongs to the OKF layer (type, title, x-ai-rulez) and is therefore never ai-rulez metadata.

func IsRootConfigDir added in v5.5.0

func IsRootConfigDir(dir string) bool

IsRootConfigDir reports whether dir is the project root of a configuration kept in ai-rulez.toml: a directory that holds that file and is neither the .agents directory (whose ai-rulez.toml is the "agents" source) nor a config directory.

func IsSafeHookScript

func IsSafeHookScript(script string) bool

IsSafeHookScript reports whether a hook `script` path uses only the characters ai-rulez is willing to put on a shell command line.

func IsSensitiveEnvName

func IsSensitiveEnvName(name string) bool

IsSensitiveEnvName reports whether an environment variable (or key) name looks like it holds a secret.

func IsSensitiveHeaderName

func IsSensitiveHeaderName(name string) bool

IsSensitiveHeaderName reports whether an HTTP header name carries a credential.

func IsValidCheckName

func IsValidCheckName(name string) bool

IsValidCheckName reports whether name is usable as a check identity: it is a file stem restricted to [A-Za-z0-9._-], so it can be used in output paths and section markers without escaping, and it cannot be a path traversal segment.

func IsValidTriggerMode

func IsValidTriggerMode(mode string) bool

IsValidTriggerMode checks if the trigger mode is valid

func IsVersionConstraintRef

func IsVersionConstraintRef(ref string) bool

IsVersionConstraintRef reports whether ref is the shorthand for a constraint that parses (see IsVersionSugar).

func IsVersionSugar

func IsVersionSugar(ref string) bool

IsVersionSugar reports whether a ref is really a version constraint. Git forbids ^ ~ * and spaces in ref names (git check-ref-format), so a ref that contains one cannot be a ref; "1.2" or ">=1" stay plain ref names because those characters are legal in branch names.

func LayerFiles added in v5.0.5

func LayerFiles(configDir string) []string

LayerFiles lists the layer names that have a config.<name>.toml file in configDir, sorted, for tooling that shows what can be activated.

func LayerSource added in v5.2.0

func LayerSource(file string) string

LayerSource names a committed config.<name>.toml layer in merge messages.

func LegacyConfigIn

func LegacyConfigIn(configDir string) string

LegacyConfigIn returns the V2/V3 config file inside configDir (a config directory such as .ai-rulez/) when the directory has no config.toml, else "".

func LegacyFilesBeside

func LegacyFilesBeside(configDir string) []string

LegacyFilesBeside lists the 4.x files in configDir that v5 does not read and that a valid config.toml silently shadows.

func LoadRecursiveExclude added in v5.6.3

func LoadRecursiveExclude(dir, configDirName string) ([]string, error)

LoadRecursiveExclude reads recursive_exclude from the configuration of the project at dir (.agents/ai-rulez.toml, .ai-rulez/config.toml, .config/ai-rulez/config.toml or ai-rulez.toml, as discovery finds them; an explicit configDirName is honored exactly), without loading the rest of the configuration: a recursive run needs it before it knows which configurations exist. A directory with no configuration has none.

func LocalGitignorePatterns

func LocalGitignorePatterns(configDir string) []string

LocalGitignorePatterns are the stable ignore entries for every machine-local input of the project whose config directory is configDir: the overlay, its lock and temp files, and the local/ content tree. Writers of any of them ensure these before writing.

func LocalSkillSource added in v5.5.0

func LocalSkillSource(env ambient.Env, source string) (dir string, local bool, err error)

LocalSkillSource reports whether an installed_skills source is a local directory and returns it, not yet joined to the project: a "~" or "~/path" source (the home directory comes from env), and a file:// URL naming a directory that is not a git repository. Any other source is returned as written with local false; GitHub shorthand has already been expanded.

func LocalVariantPath

func LocalVariantPath(p string) string

LocalVariantPath returns the sibling path with ".local" inserted before the extension, but only for markdown files. It returns "" when p is not a ".md" file — used by preset generators to derive the machine-local root filename (CLAUDE.md → CLAUDE.local.md) while skipping presets whose root is not a single markdown file.

func MCPHarnessNames

func MCPHarnessNames() []string

MCPHarnessNames lists the presets HarnessSupportsMCP accepts, sorted.

func MarshalTOML

func MarshalTOML(cfg *Config) ([]byte, error)

MarshalTOML serializes a Config to a TOML document with a leading docs header. SaveConfig and the importer write config.toml through it.

func MergeConfigDocs

func MergeConfigDocs(shared, local map[string]any) (merged map[string]any, warnings []string, err error)

MergeConfigDocs overlays a machine-local config document onto the shared one. Both inputs are generic decoded documents (map[string]any from TOML/YAML/JSON). Inputs are never mutated; the result shares no maps/slices with them. The result uses the TOML key "schema" for the schema reference; use DocForJSON before a JSON round trip.

func MergeConfigDocsFrom added in v5.2.0

func MergeConfigDocsFrom(shared, local map[string]any, src string) (merged map[string]any, warnings []string, err error)

MergeConfigDocsFrom is MergeConfigDocs with a source label ("local config", "layer config.lint.toml") that its errors and warnings use to name the document being merged in.

func NativeContent

func NativeContent(content string) string

NativeContent returns a content file with its OKF frontmatter mapped onto the native layout: `type`, `title` and `x-ai-rulez` are gone and the entries of x-ai-rulez.metadata sit at the top level. It lets a writer edit ai-rulez fields with the code that knows the native layout. The block is found by the shared internal/frontmatter splitter (BOM, trailing spaces on a fence and CRLF as the loader reads them). Content without a closed frontmatter block is returned as it is.

func NoFetchRequested

func NoFetchRequested(ctx context.Context) bool

NoFetchRequested reports whether ctx is offline because the load's LockPolicy asked for it (--no-fetch, --frozen), rather than by design of the command.

func NormalizeFrontmatterKey

func NormalizeFrontmatterKey(key string) string

NormalizeFrontmatterKey folds a frontmatter key to its comparison form: lower case, without "-" or "_" (mcp-servers, mcp_servers and mcpServers are one key).

func NormalizeGlobs

func NormalizeGlobs(in []string) []string

NormalizeGlobs splits each element on commas that are not inside braces or brackets and not escaped with a backslash, trims whitespace, drops empties and removes duplicates, keeping first-seen order. "*.{ts,tsx}" and "src/[a,b]*.ts" stay one glob, a backslash-escaped comma is kept verbatim, and "a, b" becomes two. An unbalanced "{" or "[" suppresses splitting for the rest of that element.

func NormalizeInstalledSkillSources added in v5.5.0

func NormalizeInstalledSkillSources(baseDir string, skills []InstalledSkillConfig)

NormalizeInstalledSkillSources rewrites GitHub shorthand sources to their https URL, so every consumer (resolver, lock, version pins) sees one form and a lock key for a shorthand matches the one for the full URL. "github:owner/repo" is always a repository. A bare "owner/repo" is one only when no such path exists under baseDir, so an existing relative local source keeps meaning a directory. Any other source is left as written.

func NormalizeTelemetryEndpoint

func NormalizeTelemetryEndpoint(endpoint, protocol string) string

NormalizeTelemetryEndpoint returns the endpoint in URL form. A gRPC endpoint is documented as host[:port], so one without a scheme means https (TLS); every other protocol, and an endpoint that already has a scheme, is returned unchanged.

func OfflineIncludes

func OfflineIncludes(ctx context.Context) bool

OfflineIncludes reports whether ctx asks for cache-only include resolution.

func ParseApprovalMaxAge

func ParseApprovalMaxAge(s string) (time.Duration, error)

ParseApprovalMaxAge parses a max_age: whole days ("365d") or a Go duration ("720h").

func ParseApprovalSelector

func ParseApprovalSelector(sel string) (kind string, err error)

ParseApprovalSelector validates one require_approval entry and returns the kind for a "kind:<kind>" selector ("" for the keywords).

func ParseDiscoverSkills added in v5.5.0

func ParseDiscoverSkills(value string) (string, error)

ParseDiscoverSkills normalises a discover_skills value: empty is off, and the case and surrounding space are ignored. Any other word than off, repo or all is an error.

func ParseIndexCache added in v5.5.0

func ParseIndexCache(value string) (string, error)

ParseIndexCache normalises an index_cache value: empty is user, and the case and surrounding space are ignored. Any other word than user, repo or off is an error.

func ParseLocalPath

func ParseLocalPath(s string) ([]string, error)

ParseLocalPath splits an overlay key path such as "mcp_servers.github.env.TOKEN" into segments and checks the leading key. For named lists (mcp_servers, plugins, includes, installed_skills, marketplaces, scopes) the segment after the list is the entry name. A segment that contains a dot is written in brackets with double quotes: mcp_servers["foo.bar"].command. Inside the quotes, \" and \\ are escapes.

func ParseSigningTime

func ParseSigningTime(s string, endOfDay bool) (time.Time, error)

ParseSigningTime parses a trust entry bound: a date ("2026-01-01", UTC) or an RFC 3339 time. endOfDay makes a date bound inclusive of that whole day.

func PluginFormats added in v5.5.0

func PluginFormats() []string

PluginFormats lists the values of `format`.

func PluginGitURL added in v5.5.0

func PluginGitURL(source string) string

PluginGitURL turns a git or github-shorthand source into the URL git clones.

func PluginKinds added in v5.5.0

func PluginKinds() []string

PluginKinds lists the component kinds `include` and `exclude` accept.

func PluginPathProblem added in v5.5.0

func PluginPathProblem(p string) string

PluginPathProblem says why a path cannot address a directory inside a repository ("" when it can).

func PluginRefProblem added in v5.5.0

func PluginRefProblem(ref string) string

PluginRefProblem says why a ref cannot be handed to the fetcher ("" when it can).

func PluginSourceProblem added in v5.5.0

func PluginSourceProblem(source string) string

PluginSourceProblem says why a marketplace or plugin source is refused ("" when it is fine).

func PolicyLocks

func PolicyLocks(e PolicyEnforcer, feature string) bool

PolicyLocks reports whether the policy forbids network use of the named feature ("telemetry" or "llm"). It is false without a policy. It knows no repository, so the organization policy of --discover-org is not part of it: use LocksIn where a project directory is known.

func PolicyLocksIn

func PolicyLocksIn(e PolicyEnforcer, feature, dir string) bool

PolicyLocksIn is PolicyLocks for the project at dir: when organization discovery is on, the owner's policy can forbid telemetry export or model calls too. An enforcer that cannot discover answers as PolicyLocks does.

func PolicyWarnings

func PolicyWarnings(cfg *Config) []string

PolicyWarnings lists the violations a --policy-mode warn run reports without failing; it is nil in the default mode, where CheckPolicy fails instead.

func ProfileMatches

func ProfileMatches(profile string, scoped []string) bool

ProfileMatches reports whether an item scoped to the named profiles is active for a (possibly composed) profile value. An empty scoped list means every profile. A value with no usable elements is treated as the built-in default, which includes everything.

func ProjectBaseDir

func ProjectBaseDir(configDir string) string

ProjectBaseDir returns the project directory that owns configDir: the directory containing the config directory. For the nested .config/ai-rulez/ layout it skips past the generic .config/ wrapper so generated outputs are rooted in the project, not in .config/.

func ReadVersionFrom added in v5.0.4

func ReadVersionFrom(baseDir, versionFrom string) (string, error)

ReadVersionFrom returns the version declared by versionFrom, a project-relative path to a supported manifest. It errors when the path escapes the project, the file name is unsupported, the file is missing, or it carries no version key.

func ResolveConfigDirName

func ResolveConfigDirName(baseDir string) string

ResolveConfigDirName returns the first config directory candidate that exists under baseDir and contains a config file, or "" when none is present. The returned name is slash-separated and relative to baseDir.

func ResolveGenerationTime

func ResolveGenerationTime() time.Time

ResolveGenerationTime returns the timestamp for one generation run: the value of SOURCE_DATE_EPOCH when it holds a parsable Unix second count, otherwise the wall clock. A malformed value falls through to the clock rather than failing generation or silently stamping the epoch, which would be indistinguishable from a deliberate pin to 1970.

func ResolveGenerationTimeIn

func ResolveGenerationTimeIn(host ambient.Host) time.Time

ResolveGenerationTimeIn is ResolveGenerationTime with the environment and clock taken from host.

func ResolveLocalConfigDir

func ResolveLocalConfigDir(baseDir, configFlag, dirName string) string

ResolveLocalConfigDir returns the config directory the local commands act on, resolving the project the way loading does: configFlag (the global --config, a config directory or a file inside one) wins, then dirName (--config-dir) under baseDir, then discovery under baseDir.

func ResolveProfileDomains added in v5.0.2

func ResolveProfileDomains(entries []string, knownDomains []string) ([]string, error)

ResolveProfileDomains resolves a profile's domain entries against the domains a config knows. Positive entries are kept in the order written, "*" expands to every known domain (sorted, so the result is deterministic) and "!name" entries are removed from the result. Exact names behave as before: an unknown one is kept as written, so validation can warn about it and selection simply ignores it.

A list with exclusions but no positive entry is an error: there is nothing to subtract from, so the author almost certainly meant to write "*" first.

func RootManifestNames added in v5.5.0

func RootManifestNames() []string

RootManifestNames are the manifest file names of the root layout.

func RulesDirRemainder

func RulesDirRemainder(relPath string) (rest string, ok bool)

RulesDirRemainder is Remainder for the built-in rules folders only; use Config.RulesDirRemainder where a project's custom folders matter.

func SameDir added in v5.5.0

func SameDir(a, b string) bool

SameDir reports whether a and b are the same directory, resolving symlinks.

func SanitizeDecodeError

func SanitizeDecodeError(err error) error

SanitizeDecodeError returns an error with the offending source values removed from a decode failure, keeping line numbers and types. The merged document includes machine-local values that may be credentials, so they must not reach terminals, logs or MCP responses.

func SaveConfig

func SaveConfig(cfg *Config, configDir string) error

SaveConfig writes a configuration back to config.toml in configDir. TOML is written with MarshalTOML (comments are not preserved).

func SearchStopDir added in v5.5.0

func SearchStopDir(ctx context.Context, cwd string) string

SearchStopDir is the last directory a walk-up from cwd searches: the git work tree root, else the file system root (""), and never a directory above the home directory.

func SeedBundleListing added in v5.4.0

func SeedBundleListing(ctx context.Context, files []string)

SeedBundleListing gives the snapshots WithBundleSnapshots put in ctx the listing the caller already holds: the slash paths, relative to the anchor, of the tracked files and the untracked files git does not ignore. It saves the second `git ls-files` over the whole repository.

func SessionStartHarnesses added in v5.5.0

func SessionStartHarnesses() []string

SessionStartHarnesses lists the harnesses that get the hook. CLAUDE_CODE_REMOTE is set by Claude Code only, and only Claude Code documents SessionStart output (hookSpecificOutput.additionalContext) as session context; another harness would run a command that never does anything, so it is skipped, silently.

func SkillDeliveryValue

func SkillDeliveryValue(skill ContentFile) string

SkillDeliveryValue returns the raw `delivery` frontmatter value of a skill, or "".

func SkillDescription

func SkillDescription(metadata *Metadata) string

SkillDescription extracts the explicit description from skill frontmatter.

func SkillDescriptionForContent

func SkillDescriptionForContent(skill ContentFile) string

SkillDescriptionForContent returns the explicit skill description or falls back to the skill ID.

func SkillDescriptionOrFallback

func SkillDescriptionOrFallback(description, skillID string) string

SkillDescriptionOrFallback returns an explicit description when present, otherwise falls back to the skill ID.

func SkillID

func SkillID(skill ContentFile) string

SkillID returns the canonical skill identifier from the path when available, otherwise the content name.

func SkillOverrideValues

func SkillOverrideValues() []string

SkillOverrideValues returns the documented skillOverrides states.

func SkillShortDescription

func SkillShortDescription(metadata *Metadata) string

SkillShortDescription extracts an optional short-description from skill frontmatter.

func SkillSpecKeys

func SkillSpecKeys() []string

SkillSpecKeys returns the optional Agent Skills specification frontmatter keys that presets carry through to generated SKILL.md files.

func SkipOutputValues added in v5.5.0

func SkipOutputValues() []string

SkipOutputValues lists the values skip_outputs accepts.

func SplitProfileNames

func SplitProfileNames(value string) []string

SplitProfileNames splits a profile value into its element names.

A composed value selects the union of several profiles' domains, which is what lets one profile hold the content everybody gets while the others add role specifics — without a combinatorial profile per role pair.

Whitespace around an element is trimmed and empty elements are ignored, so "base, backend" and "base,backend," both resolve to the same two names. A value made up entirely of separators yields no names at all; callers treat that as a profile that does not exist rather than silently falling back, so a mistyped value is reported instead of quietly generating something else.

Elements are profile names, never domain names, and a profile value may not reference another profile: composition is one level deep by construction, which is why no cycle detection is needed anywhere.

func SplitRoleNames added in v5.0.1

func SplitRoleNames(value string) []string

SplitRoleNames splits a comma-separated --role value into role names. Role names never contain a comma, so composition is unambiguous. Whitespace and empty elements are dropped and duplicates removed, preserving order.

func SummarizeResourceDescription

func SummarizeResourceDescription(text string) string

SummarizeResourceDescription reduces text to a single-line summary: control characters become spaces, whitespace collapses, the first sentence is kept and the result is cut at a rune boundary to MaxResourceDescriptionRunes.

func UnknownPresetError

func UnknownPresetError(cfg *Config) error

UnknownPresetError is the error for the first built-in preset of cfg that does not exist, with the closest name when there is one; nil when every built-in preset is known. A schema check reports the same typo as a dump of the preset schema's alternatives, which this explains in one line.

func UnresolvedIncludesTolerated

func UnresolvedIncludesTolerated(ctx context.Context) bool

UnresolvedIncludesTolerated reports whether ctx tolerates unresolved includes.

func UserConfigDir

func UserConfigDir(getenv func(string) string, home string) string

UserConfigDir is the user-level ai-rulez directory: $XDG_CONFIG_HOME/ai-rulez when XDG_CONFIG_HOME is absolute, else <home>/.config/ai-rulez. home "" means the current user's home directory; getenv nil means os.Getenv. It returns "" when no home directory can be determined. The user config file (see UserConfigFile) and user scope share this one resolution.

func UserConfigFile

func UserConfigFile(getenv func(string) string) string

UserConfigFile is the path of the user config file, "" when no home directory can be determined. Every reader of it (the llm and [telemetry] resolvers) goes through here, so the location is defined once.

func ValidChannel

func ValidChannel(name string) bool

ValidChannel reports whether name is a usable marketplace channel name.

func ValidOCIRepository

func ValidOCIRepository(ref string) bool

ValidOCIRepository reports whether ref is a repository reference without a tag or digest.

func ValidTelemetryProtocol

func ValidTelemetryProtocol(p string) bool

ValidTelemetryProtocol reports whether p is one of the implemented protocols.

func ValidateExcludeGlobs added in v5.5.1

func ValidateExcludeGlobs(globs []string) error

ValidateExcludeGlobs checks the globs of [mcp] exclude and of `mcp --exclude`: each is non-empty, stays inside the project and compiles.

func ValidateIdentityRegexp

func ValidateIdentityRegexp(expr string) error

ValidateIdentityRegexp checks that expr compiles and is anchored at both ends: an unanchored pattern accepts any identity that merely contains it (AR722).

func ValidateInstalledMarketplaces added in v5.5.0

func ValidateInstalledMarketplaces(list []InstalledMarketplaceConfig) error

ValidateInstalledMarketplaces validates the installed_marketplaces section: required fields, unique names, and a source, ref and path that are safe to hand to git or an HTTP client.

func ValidateInstalledPlugins added in v5.5.0

func ValidateInstalledPlugins(plugins []InstalledPluginConfig, marketplaces []InstalledMarketplaceConfig) error

ValidateInstalledPlugins validates the installed_plugins section against the declared marketplaces.

func ValidateInstalledSkillFields

func ValidateInstalledSkillFields(skill *InstalledSkillConfig) error

ValidateInstalledSkillFields rejects values that would be read as a git option or transport helper, or that escape the skill repository.

func ValidateInstalledSkillVersion added in v5.5.0

func ValidateInstalledSkillVersion(skill *InstalledSkillConfig) error

ValidateInstalledSkillVersion checks the version-constraint keys of an entry (ref as a constraint, version, tag_prefix, include_prerelease, min_release_age), the check config load applies.

func ValidateInstalledSkills

func ValidateInstalledSkills(skills []InstalledSkillConfig) error

ValidateInstalledSkills validates the installed_skills section: required fields, unique names, and a source, ref and path that are safe to hand to git.

func ValidateOutputPath

func ValidateOutputPath(key, dir string) error

ValidateOutputPath rejects a config-supplied output path (file or directory) that could write outside the project or over VCS or ai-rulez control data. A hostile repository controls these settings, so the check is an allowlist of shape: a non-empty relative slash path, no NUL, no ".." segment, no drive or UNC prefix, no segment naming a git directory (".git", case-insensitively, including the Windows spellings ".git." and "git~1"), and a first segment that is not .ai-rulez, .config/ai-rulez, .hg or .svn.

func ValidateOutputSubdir

func ValidateOutputSubdir(key, dir string) error

ValidateOutputSubdir rejects an output directory setting that is absolute, leaves the project root, or points into a control directory (see ValidateOutputPath).

func ValidateScopePath

func ValidateScopePath(scopePath string) error

ValidateScopePath checks that a [[scopes]] path names a subdirectory of the project: relative, without ".." segments, and free of glob metacharacters because the path becomes a glob prefix and a file-name qualifier.

func ValidateTelemetryEndpoint

func ValidateTelemetryEndpoint(endpoint string) string

ValidateTelemetryEndpoint returns a problem description for an endpoint that is not an http(s) URL without credentials, or that is plain http to a non-loopback host. Empty means acceptable.

func ValidateTelemetryEnvName

func ValidateTelemetryEnvName(name string) string

ValidateTelemetryEnvName rejects anything that is not an environment variable NAME, including values that look like a pasted credential: an upper-case name of 20 or more characters with no underscore (an access-key id), or any string with lower case, dashes, spaces or an equals sign (a token, "Bearer x", "k=v").

func ValidateTelemetryResource

func ValidateTelemetryResource(resource map[string]string) []string

ValidateTelemetryResource returns one message per invalid label of a resource table.

func VersionFromSources added in v5.0.4

func VersionFromSources() []string

VersionFromSources lists, sorted, the file names version_from can read. It is the allowlist the resolver and its error hints share.

func WithBundleSnapshots added in v5.4.0

func WithBundleSnapshots(ctx context.Context, anchor string) context.Context

WithBundleSnapshots returns ctx carrying one set of git answers about which files below anchor (the repository top) are part of the project. Every LoadSkillDir under that context reads them instead of asking git per skill, which on a large repository is two processes per skill. Use it for a read-only scan: the answers are not refreshed.

func WithNoFetch

func WithNoFetch(ctx context.Context) context.Context

WithNoFetch marks ctx offline because the load's policy asked for it (LockPolicy.Offline), which error messages name differently from a command that never fetches by design (WithOfflineIncludes).

func WithOfflineIncludes

func WithOfflineIncludes(ctx context.Context) context.Context

WithOfflineIncludes marks ctx so include and skill sources resolve from their cache only and never touch the network. Validation loads use it.

func WithPolicyContext

func WithPolicyContext(ctx context.Context, e PolicyEnforcer) context.Context

WithPolicyContext returns ctx carrying e, the policy loads made with ctx use unless WithPolicy names another. A nil e leaves ctx unchanged.

func WithSkillBundleBudget added in v5.5.0

func WithSkillBundleBudget(ctx context.Context) context.Context

WithSkillBundleBudget starts the running total of one installed_skills entry: every skill scanned with the returned context counts against the same aggregate file and byte cap.

func WithSkillResources added in v5.5.0

func WithSkillResources(ctx context.Context, patterns []string) context.Context

WithSkillResources carries the `resources` patterns of an installed_skills entry to the scan of its skill directories (LoadSkillResourcesContext), which bundles the matching files besides references/, scripts/ and assets/. They replace DefaultInstalledSkillResources.

func WithUnresolvedIncludesTolerated

func WithUnresolvedIncludesTolerated(ctx context.Context) context.Context

WithUnresolvedIncludesTolerated marks ctx so an include that cannot be resolved is a warning and the load goes on without it. Commands that inventory or compare sources (lock, sbom) use it: they report the missing include themselves and render nothing.

func WriteFileAtomic

func WriteFileAtomic(path string, data []byte, perm os.FileMode) error

WriteFileAtomic writes data to path with perm via an exclusively created temp file in the same directory, then renames it over the target.

Types

type ARDConfig

type ARDConfig struct {
	// Publisher is the fully qualified domain name the identifiers are anchored
	// to, normally the domain that serves /.well-known/ard.json.
	Publisher string `yaml:"publisher,omitempty" json:"publisher,omitempty" toml:"publisher,omitempty"`
	// Namespace is the identifier segment between publisher and name; colons
	// separate sub-segments.
	Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty" toml:"namespace,omitempty"`
	// BaseURL is the https URL the published skill files and server cards are
	// served from. Without it entries point at the release of the repository.
	BaseURL string `yaml:"base_url,omitempty" json:"base_url,omitempty" toml:"base_url,omitempty"` //nolint:tagliatelle
	// PluginType overrides the media type of plugin entries, for when the spec
	// registers one.
	PluginType string `yaml:"plugin_type,omitempty" json:"plugin_type,omitempty" toml:"plugin_type,omitempty"` //nolint:tagliatelle
	// Queries sets the representativeQueries (2 to 5) of a skill, MCP server or
	// plugin by name, replacing the ones derived from skill frontmatter and eval
	// cases. An MCP server has nothing to derive them from.
	Queries map[string][]string `yaml:"queries,omitempty" json:"queries,omitempty" toml:"queries,omitempty"`
}

ARDConfig is the ard table (docs/ard.md): who publishes the Agentic Resource Discovery manifest and under which namespace. Identifiers are urn:air:<publisher>:<namespace>:<name>.

type Activation

type Activation struct {
	Mode        ActivationMode
	Globs       []string
	Description string
	// Source records which field decided Mode: "activation", "trigger",
	// "alwaysApply", or "derived".
	Source string
}

Activation is the resolved activation of a rule or context file.

type ActivationMode

type ActivationMode string

ActivationMode says when a rule or context file applies.

const (
	ActivationAlways ActivationMode = "always" // applies in every interaction
	ActivationGlob   ActivationMode = "glob"   // applies when matching files are in play
	ActivationAuto   ActivationMode = "auto"   // the model decides from the description
	ActivationManual ActivationMode = "manual" // applies only when explicitly invoked
)

Activation modes.

func (ActivationMode) IsValid

func (a ActivationMode) IsValid() bool

IsValid reports whether the mode is one of the four known modes.

type AgentsMDInlining

type AgentsMDInlining struct {
	Scoped     bool
	AutoManual bool
}

AgentsMDInlining says which items beyond the always-on ones the shared AGENTS.md inlines. Scoped covers glob, auto and manual rules and glob context; AutoManual only the auto and manual ones.

func SharedAgentsMDInlining

func SharedAgentsMDInlining(cfg *Config) AgentsMDInlining

SharedAgentsMDInlining decides what AGENTS.md carries beyond always-on rules and context: scoped items only when a configured preset relying on the file has no rules folder that holds them (the folder presets keep their own), the auto and manual ones when a folder cannot hold those.

type AgentsSourceReader added in v5.2.0

type AgentsSourceReader func(ctx context.Context, cfg *Config, tree *ContentTree) (*ContentTree, error)

AgentsSourceReader completes the content tree the "agents" source scanned from .agents: it adds the root AGENTS.md as context and drops what an earlier generate run wrote there (a generated file is output, never source). The importer package supplies it (config cannot import it); see Resolvers.

type AnalysisCollector

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

AnalysisCollector accumulates per-output classification and per-section content while presets render.

It is nil on the normal generate path, and every method tolerates a nil receiver, so generation pays nothing for a facility only the cost report uses. Generation is single-goroutine per config, so the collector is not synchronized; a fresh collector belongs to a single Generator run.

func NewAnalysisCollector

func NewAnalysisCollector() *AnalysisCollector

NewAnalysisCollector returns an empty, enabled collector.

func (*AnalysisCollector) Analyses

func (c *AnalysisCollector) Analyses() []*OutputAnalysis

Analyses returns every recorded analysis in registration order.

func (*AnalysisCollector) Attribute

func (c *AnalysisCollector) Attribute(preset, baseDir string, outputs []OutputFile)

Attribute fills in the outputs a renderer did not claim. Preset name comes from the caller, which always knows it; kind is inferred from the path, which is a documented approximation — only the DSL-backed providers report an exact kind and a per-section breakdown.

func (*AnalysisCollector) Begin

func (c *AnalysisCollector) Begin(path, preset string, kind OutputKind, itemID, sourcePath string) *OutputAnalysis

Begin registers an output and returns the handle renderers record parts against. Returns nil when the collector is nil, which AddPart tolerates. Re-registering a path returns the existing handle so two presets writing the same file do not produce two entries — matching flattenPresetOutputs, which keeps one OutputFile per path.

func (*AnalysisCollector) Claimants

func (c *AnalysisCollector) Claimants(path string) []string

Claimants returns every preset that offered the output path, in first-offer order.

func (*AnalysisCollector) DeclareListing

func (c *AnalysisCollector) DeclareListing(preset string, spec *ListingSpec)

DeclareListing records the listing model a provider-DSL generator declares. A nil spec is ignored.

func (*AnalysisCollector) EnterScope

func (c *AnalysisCollector) EnterScope(scope string)

EnterScope stamps every subsequently begun analysis with the given scope label. Pass "" to return to the repository root.

func (*AnalysisCollector) Get

func (c *AnalysisCollector) Get(path string) *OutputAnalysis

Get returns the analysis for a path, or nil.

func (*AnalysisCollector) ListingFor

func (c *AnalysisCollector) ListingFor(preset string) ListingSpec

ListingFor resolves the listing model of a preset: the provider spec's own declaration first, then the builtin table. The zero value lists nothing.

type Author

type Author struct {
	Name  string `yaml:"name,omitempty" json:"name,omitempty" toml:"name,omitempty"`
	Email string `yaml:"email,omitempty" json:"email,omitempty" toml:"email,omitempty"`
	URL   string `yaml:"url,omitempty" json:"url,omitempty" toml:"url,omitempty"`
}

Author identifies a person or organization in plugin/marketplace metadata.

type BazRulesConfig added in v5.0.4

type BazRulesConfig struct {
	// Include names the rules and context files the baz preset selects for its
	// path-scoped AGENTS.md files when rules.baz_default is "none". The same
	// opt-in is `baz: true` in an item's frontmatter.
	Include []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`
}

BazRulesConfig is the [rules.baz] table.

type BuiltinsConfig

type BuiltinsConfig struct {
	All   *bool    // true = all, false = none
	Names []string // specific builtin names (when All is nil)
}

BuiltinsConfig represents the builtins field which can be:

  • boolean true: enable all builtins
  • boolean false: disable all builtins (including auto-includes)
  • array of strings: enable specific builtins (supports "!name" exclusion)

func (*BuiltinsConfig) GetNames

func (b *BuiltinsConfig) GetNames() []string

GetNames returns the list of builtin names (empty if All is set)

func (*BuiltinsConfig) IsAll

func (b *BuiltinsConfig) IsAll() bool

IsAll returns true if all builtins should be loaded

func (*BuiltinsConfig) IsEnabled

func (b *BuiltinsConfig) IsEnabled() bool

IsEnabled returns true if builtins are configured (not nil)

func (*BuiltinsConfig) IsNone

func (b *BuiltinsConfig) IsNone() bool

IsNone returns true if all builtins should be disabled

func (BuiltinsConfig) MarshalJSON

func (b BuiltinsConfig) MarshalJSON() ([]byte, error)

MarshalJSON implements custom JSON marshaling for BuiltinsConfig

func (*BuiltinsConfig) UnmarshalJSON

func (b *BuiltinsConfig) UnmarshalJSON(data []byte) error

UnmarshalJSON implements custom JSON unmarshaling for BuiltinsConfig

type CatalogConfig

type CatalogConfig struct {
	// Title is the title of the static site.
	Title string `yaml:"title,omitempty" json:"title,omitempty" toml:"title,omitempty"`
	// IncludeExcerpt sets whether items carry a body excerpt. Unset: on, except
	// for an indexable site.
	IncludeExcerpt *bool `yaml:"include_excerpt,omitempty" json:"include_excerpt,omitempty" toml:"include_excerpt,omitempty"` //nolint:tagliatelle
	// ExcludeOwners leaves owner names out of the catalog and the site.
	ExcludeOwners bool `yaml:"exclude_owners,omitempty" json:"exclude_owners,omitempty" toml:"exclude_owners,omitempty"` //nolint:tagliatelle
	// Indexable lets search engines index the site (no robots.txt, no noindex).
	Indexable bool `yaml:"indexable,omitempty" json:"indexable,omitempty" toml:"indexable,omitempty"`
	// MaxItemsPerPage is how many overview rows one page of the site shows
	// before the rest moves to the next page. 0 means DefaultCatalogPageSize.
	MaxItemsPerPage int `yaml:"max_items_per_page,omitempty" json:"max_items_per_page,omitempty" toml:"max_items_per_page,omitempty"` //nolint:tagliatelle
	// RenderMarkdown renders item excerpts as sanitized Markdown instead of plain
	// text.
	RenderMarkdown bool `yaml:"render_markdown,omitempty" json:"render_markdown,omitempty" toml:"render_markdown,omitempty"` //nolint:tagliatelle
}

CatalogConfig is the [catalog] table: defaults for `ai-rulez catalog` (see docs/catalog.md). Every key is optional; a command-line flag overrides it.

type CatalogSkillConfig

type CatalogSkillConfig struct {
	Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty" toml:"enabled,omitempty"`
	// Name is the skill name; empty means "plugin-catalog".
	Name string `yaml:"name,omitempty" json:"name,omitempty" toml:"name,omitempty"`
	// Description is the skill description shown to the model.
	Description string `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
}

CatalogSkillConfig is the [marketplace.catalog_skill] block.

func (*CatalogSkillConfig) SkillName

func (c *CatalogSkillConfig) SkillName() string

SkillName returns the configured or default catalog skill name.

type ClaudeConfig

type ClaudeConfig struct {
	Settings *ClaudeSettings `yaml:"settings,omitempty" json:"settings,omitempty" toml:"settings,omitempty"`
	Skills   *ClaudeSkills   `yaml:"skills,omitempty" json:"skills,omitempty" toml:"skills,omitempty"`
	// ClaudeMD controls the CLAUDE.md file the claude preset writes when
	// agents_md is on: empty or "shim" keeps the "@AGENTS.md" import file,
	// "none" writes only AGENTS.md (which Claude Code >= 2.1.277 reads
	// directly) and removes a previously generated shim.
	ClaudeMD string `yaml:"claude_md,omitempty" json:"claude_md,omitempty" toml:"claude_md,omitempty"` //nolint:tagliatelle
}

ClaudeConfig groups Claude Code specific output options.

func (*ClaudeConfig) ClaudeMDOrEmpty added in v5.0.2

func (c *ClaudeConfig) ClaudeMDOrEmpty() string

ClaudeMDOrEmpty returns the configured claude_md mode, or "" when the [claude] table is absent. Safe on a nil receiver.

func (*ClaudeConfig) HidesSkillsFromMenu

func (c *ClaudeConfig) HidesSkillsFromMenu() bool

HidesSkillsFromMenu reports whether [claude.skills] hide_from_menu is set. Safe on a nil receiver.

func (*ClaudeConfig) OmitsClaudeShim added in v5.0.2

func (c *ClaudeConfig) OmitsClaudeShim() bool

OmitsClaudeShim reports whether claude_md = "none" turns off the CLAUDE.md shim, so the claude preset relies on the shared AGENTS.md alone. Safe on a nil receiver.

type ClaudeSettings

type ClaudeSettings struct {
	Manage bool `yaml:"manage,omitempty" json:"manage,omitempty" toml:"manage,omitempty"`
	// RegisterMarketplace owns extraKnownMarketplaces.<marketplace.name>.
	// Defaults to true when Manage is set.
	RegisterMarketplace *bool `yaml:"register_marketplace,omitempty" json:"register_marketplace,omitempty" toml:"register_marketplace,omitempty"` //nolint:tagliatelle
	// MarketplaceSource overrides the default directory source.
	MarketplaceSource *MarketplaceSource `yaml:"marketplace_source,omitempty" json:"marketplace_source,omitempty" toml:"marketplace_source,omitempty"` //nolint:tagliatelle
	AutoUpdate        *bool              `yaml:"auto_update,omitempty" json:"auto_update,omitempty" toml:"auto_update,omitempty"`                      //nolint:tagliatelle
	// EnablePlugins and DisablePlugins own enabledPlugins."<name>@<marketplace>"
	// as true and false. Names are plugin names in the marketplace.
	EnablePlugins  []string `yaml:"enable_plugins,omitempty" json:"enable_plugins,omitempty" toml:"enable_plugins,omitempty"`    //nolint:tagliatelle
	DisablePlugins []string `yaml:"disable_plugins,omitempty" json:"disable_plugins,omitempty" toml:"disable_plugins,omitempty"` //nolint:tagliatelle
	// Managed owns further keys of .claude/settings.json entry by entry (env,
	// skillOverrides). It needs no Manage flag.
	Managed *ManagedSettings `yaml:"managed,omitempty" json:"managed,omitempty" toml:"managed,omitempty"`
}

ClaudeSettings is the [claude.settings] block: opt-in management of the plugin keys of .claude/settings.json. Only the listed entries are owned.

func (*ClaudeSettings) RegistersMarketplace

func (s *ClaudeSettings) RegistersMarketplace() bool

RegistersMarketplace reports whether extraKnownMarketplaces is owned.

type ClaudeSkills

type ClaudeSkills struct {
	// HideFromMenu writes `user-invocable: false` on every skill that does not
	// set the key itself, so Claude Code hides them from the / menu and only the
	// model loads them. Off by default: a skill without the key is user-invocable.
	HideFromMenu bool `yaml:"hide_from_menu,omitempty" json:"hide_from_menu,omitempty" toml:"hide_from_menu,omitempty"` //nolint:tagliatelle
}

ClaudeSkills is the [claude.skills] block: options for the skills written to .claude/skills.

type CodexConfig

type CodexConfig struct {
	// ProjectDocMaxBytes is the project_doc_max_bytes your Codex is configured
	// with; generate warns when the AGENTS.md chain exceeds it. Unset means the
	// Codex default (32 KiB); 0 or a negative value turns the warning off.
	ProjectDocMaxBytes *int `yaml:"project_doc_max_bytes,omitempty" json:"project_doc_max_bytes,omitempty" toml:"project_doc_max_bytes,omitempty"` //nolint:tagliatelle
}

CodexConfig is the [codex] block.

func (*CodexConfig) ProjectDocLimit

func (c *CodexConfig) ProjectDocLimit() int

ProjectDocLimit returns the AGENTS.md byte budget to warn against, or 0 when the warning is disabled. Safe on a nil receiver.

type CodexExtras

type CodexExtras struct {
	// Manifest is legacy (default), root or both; see the CodexManifest constants.
	Manifest string `yaml:"manifest,omitempty" json:"manifest,omitempty" toml:"manifest,omitempty"`
	// Marketplace also writes the Codex marketplace index
	// (.agents/plugins/marketplace.json) for a single-plugin repository.
	Marketplace bool `yaml:"marketplace,omitempty" json:"marketplace,omitempty" toml:"marketplace,omitempty"`
}

CodexExtras selects how the Codex runtime is laid out.

func (*CodexExtras) ManifestLayout

func (c *CodexExtras) ManifestLayout() string

ManifestLayout returns the resolved Codex manifest layout.

type Config

type Config struct {
	Schema      string   `yaml:"$schema,omitempty" json:"$schema,omitempty" toml:"schema,omitempty"`
	Version     string   `yaml:"version" json:"version" toml:"version"`
	Name        string   `yaml:"name" json:"name" toml:"name"`
	Description string   `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	Presets     []Preset `yaml:"presets,omitempty" json:"presets,omitempty" toml:"presets,omitempty"`
	// RecursiveExclude lists directories, relative to this project root, that
	// `generate --recursive` run from here never searches for configuration: a
	// glob such as "svc/*/gen" or a plain directory such as "tests/fixtures". The
	// --exclude flag adds to it.
	RecursiveExclude []string `yaml:"recursive_exclude,omitempty" json:"recursive_exclude,omitempty" toml:"recursive_exclude,omitempty"`
	// DefaultLayers names config.<name>.toml files beside config.toml (in .agents/.ai-rulez/
	// for an .agents project) that are always
	// merged over it, in order, before the layers AI_RULEZ_LAYERS names. A listed layer
	// with no file is an error. The key is ignored inside a layer.
	DefaultLayers []string            `yaml:"default_layers,omitempty" json:"default_layers,omitempty" toml:"default_layers,omitempty"` //nolint:tagliatelle
	Default       string              `yaml:"default,omitempty" json:"default,omitempty" toml:"default,omitempty"`
	Profiles      map[string][]string `yaml:"profiles,omitempty" json:"profiles,omitempty" toml:"profiles,omitempty"`
	Gitignore     *bool               `yaml:"gitignore,omitempty" json:"gitignore,omitempty" toml:"gitignore,omitempty"`
	// GitignoreExclude lists ignore patterns kept out of the managed .gitignore
	// block: generated outputs the project commits on purpose. Machine-local and
	// secret entries are never excluded.
	GitignoreExclude []string               `yaml:"gitignore_exclude,omitempty" json:"gitignore_exclude,omitempty" toml:"gitignore_exclude,omitempty"` //nolint:tagliatelle
	Includes         []IncludeConfig        `yaml:"includes,omitempty" json:"includes,omitempty" toml:"includes,omitempty"`
	InstalledSkills  []InstalledSkillConfig `yaml:"installed_skills,omitempty" json:"installed_skills,omitempty" toml:"installed_skills,omitempty"` //nolint:tagliatelle
	// InstalledMarketplaces and InstalledPlugins import the content of Claude
	// Code, Codex, Cursor and Gemini plugins, read through plugin marketplaces
	// or straight from a repository (see docs/installed-plugins.md).
	InstalledMarketplaces []InstalledMarketplaceConfig `yaml:"installed_marketplaces,omitempty" json:"installed_marketplaces,omitempty" toml:"installed_marketplaces,omitempty"` //nolint:tagliatelle
	InstalledPlugins      []InstalledPluginConfig      `yaml:"installed_plugins,omitempty" json:"installed_plugins,omitempty" toml:"installed_plugins,omitempty"`                //nolint:tagliatelle
	Header                *HeaderConfig                `yaml:"header,omitempty" json:"header,omitempty" toml:"header,omitempty"`
	Defaults              *DefaultsConfig              `yaml:"defaults,omitempty" json:"defaults,omitempty" toml:"defaults,omitempty"`
	Builtins              *BuiltinsConfig              `yaml:"builtins,omitempty" json:"builtins,omitempty" toml:"builtins,omitempty"`
	// SkipOutputs lists output kinds ai-rulez never writes, overwrites or deletes
	// ("instructions", "agents_md", "claude_md", "rules", "skills", "agents",
	// "commands"); see skip_outputs.go.
	SkipOutputs []string `yaml:"skip_outputs,omitempty" json:"skip_outputs,omitempty" toml:"skip_outputs,omitempty"` //nolint:tagliatelle
	Compact     *bool    `yaml:"compact,omitempty" json:"compact,omitempty" toml:"compact,omitempty"`
	AgentsMD    bool     `yaml:"agents_md,omitempty" json:"agents_md,omitempty" toml:"agents_md,omitempty"` //nolint:tagliatelle
	// Source selects where the content is read from: "ai-rulez" (default, the
	// .ai-rulez content tree) or "agents" (AGENTS.md and .agents/, see agents_source.go).
	Source string `yaml:"source,omitempty" json:"source,omitempty" toml:"source,omitempty"`
	// AgentsMDSplit makes the "agents" source read AGENTS.md as one context item
	// per H2 heading (named agents-<heading slug>) instead of one item.
	AgentsMDSplit bool `yaml:"agents_md_split,omitempty" json:"agents_md_split,omitempty" toml:"agents_md_split,omitempty"`
	// BundleExclude adds patterns to DefaultBundleExcludes: skill and command
	// resources matching one are not listed in SKILL.md or copied.
	BundleExclude []string `yaml:"bundle_exclude,omitempty" json:"bundle_exclude,omitempty" toml:"bundle_exclude,omitempty"` //nolint:tagliatelle
	// CodexSkillsDir is where the codex preset writes skills, relative to the
	// output base dir. Empty means the documented ".agents/skills"; set
	// ".codex/skills" to keep the pre-4.24 location.
	CodexSkillsDir string              `yaml:"codex_skills_dir,omitempty" json:"codex_skills_dir,omitempty" toml:"codex_skills_dir,omitempty"` //nolint:tagliatelle
	Plugins        []PluginConfig      `yaml:"plugins,omitempty" json:"plugins,omitempty" toml:"plugins,omitempty"`
	Marketplaces   []MarketplaceConfig `yaml:"marketplaces,omitempty" json:"marketplaces,omitempty" toml:"marketplaces,omitempty"`
	// PluginsAuthoring and MarketplacesAuthoring are the producer-side definitions
	// of several plugins and several marketplace indexes from one root. They are
	// distinct from the consumer-side Plugins and Marketplaces above.
	PluginsAuthoring      []MarketplacePlugin     `yaml:"plugins_authoring,omitempty" json:"plugins_authoring,omitempty" toml:"plugins_authoring,omitempty"`                //nolint:tagliatelle
	MarketplacesAuthoring []MarketplaceDefinition `yaml:"marketplaces_authoring,omitempty" json:"marketplaces_authoring,omitempty" toml:"marketplaces_authoring,omitempty"` //nolint:tagliatelle
	Scopes                []ScopeConfig           `yaml:"scopes,omitempty" json:"scopes,omitempty" toml:"scopes,omitempty"`
	MCP                   *MCPConfig              `yaml:"mcp,omitempty" json:"mcp,omitempty" toml:"mcp,omitempty"`
	Rules                 *RulesConfig            `yaml:"rules,omitempty" json:"rules,omitempty" toml:"rules,omitempty"`
	Lint                  *LintConfig             `yaml:"lint,omitempty" json:"lint,omitempty" toml:"lint,omitempty"`
	OKF                   *OKFConfig              `yaml:"okf,omitempty" json:"okf,omitempty" toml:"okf,omitempty"`
	LLMsTxt               *LLMsTxtConfig          `yaml:"llms_txt,omitempty" json:"llms_txt,omitempty" toml:"llms_txt,omitempty"` //nolint:tagliatelle
	Usage                 *UsageConfig            `yaml:"usage,omitempty" json:"usage,omitempty" toml:"usage,omitempty"`
	// Roles map a job to a slice of the shared content (see roles.go); RoleManifest
	// is the [role_manifest] table, kept apart because [[roles]] is an array.
	Roles        []RoleConfig        `yaml:"roles,omitempty" json:"roles,omitempty" toml:"roles,omitempty"`
	RoleManifest *RoleManifestConfig `yaml:"role_manifest,omitempty" json:"role_manifest,omitempty" toml:"role_manifest,omitempty"` //nolint:tagliatelle
	// Lock configures content pinning in ai-rulez.lock (see internal/contentlock).
	Lock *LockConfig `yaml:"lock,omitempty" json:"lock,omitempty" toml:"lock,omitempty"`
	// Governance is the [governance] table: which content needs a reviewer approval (types_governance.go).
	Governance *GovernanceConfig `yaml:"governance,omitempty" json:"governance,omitempty" toml:"governance,omitempty"`
	// Catalog is the [catalog] table: defaults for `ai-rulez catalog` (types_catalog.go).
	Catalog *CatalogConfig `yaml:"catalog,omitempty" json:"catalog,omitempty" toml:"catalog,omitempty"`
	// Signing is the [signing] table: who may sign the lock and how fresh the signature must be (types_signing.go).
	Signing *SigningConfig `yaml:"signing,omitempty" json:"signing,omitempty" toml:"signing,omitempty"`
	// Publish is the [publish] table: what `ai-rulez publish` ships and its policy gates (types_publish.go).
	Publish *PublishConfig `yaml:"publish,omitempty" json:"publish,omitempty" toml:"publish,omitempty"`
	// ARD is the [ard] table: the publisher and namespace of the Agentic Resource Discovery manifest (types_ard.go).
	ARD *ARDConfig `yaml:"ard,omitempty" json:"ard,omitempty" toml:"ard,omitempty"`

	// Dynamic skill loading (types_dynamic.go, delivery.go).
	Skills         *SkillsConfig           `yaml:"skills,omitempty" json:"skills,omitempty" toml:"skills,omitempty"`
	DomainSettings map[string]DomainConfig `yaml:"domains,omitempty" json:"domains,omitempty" toml:"domains,omitempty"`
	// Delivery is the [delivery] table; [delivery.stub] customizes the generated
	// dynamic-skills stub (delivery_stub.go).
	Delivery *DeliveryConfig `yaml:"delivery,omitempty" json:"delivery,omitempty" toml:"delivery,omitempty"`

	SkillSources []SkillSourceConfig `yaml:"skill_sources,omitempty" json:"skill_sources,omitempty" toml:"skill_sources,omitempty"` //nolint:tagliatelle
	// ServeMode is set while a skills server renders: every skill is rendered,
	// served ones included, and no dynamic-skills stub is added.
	ServeMode bool `yaml:"-" json:"-" toml:"-"`
	// QuietDeliveryWarnings keeps GeneratePresets from logging the AR992
	// fallback warnings: a check that renders only to inspect the result (the
	// validate stub check) reports them as findings instead.
	QuietDeliveryWarnings bool `yaml:"-" json:"-" toml:"-"`

	// LLM configures model access for features that call a model; nothing calls out unless allow_network is true.
	LLM       *llm.Config      `yaml:"llm,omitempty" json:"llm,omitempty" toml:"llm,omitempty"`
	Telemetry *TelemetryConfig `yaml:"telemetry,omitempty" json:"telemetry,omitempty" toml:"telemetry,omitempty"`
	// Review configures `ai-rulez review` (rubric, content mode, exclusions, spend ceilings).
	Review *ReviewConfig `yaml:"review,omitempty" json:"review,omitempty" toml:"review,omitempty"`
	// Judge configures `ai-rulez judge` (categories, gating, attached conventions, rubric skills, prompts, suppressions); docs/judge.md.
	Judge *JudgeConfig `yaml:"judge,omitempty" json:"judge,omitempty" toml:"judge,omitempty"`
	// Improve configures `ai-rulez improve run` defaults: gate thresholds, the optimizer and its isolation (types_improve.go).
	Improve *ImproveConfig `yaml:"improve,omitempty" json:"improve,omitempty" toml:"improve,omitempty"`
	// Search configures `ai-rulez search` and find_skill ranking: lexical by default, optionally hybrid with embeddings (docs/search.md).
	Search *skillsearch.Config `yaml:"search,omitempty" json:"search,omitempty" toml:"search,omitempty"`
	// Verifiers declares deterministic repo checks run by `ai-rulez verifiers run`.
	Verifiers []VerifierConfig `yaml:"verifiers,omitempty" json:"verifiers,omitempty" toml:"verifiers,omitempty"`
	// VerifiersSettings holds the limits and policy of `verifiers run` ([verifiers_settings]).
	VerifiersSettings *VerifiersSettings `yaml:"verifiers_settings,omitempty" json:"verifiers_settings,omitempty" toml:"verifiers_settings,omitempty"`

	// Plugin / Marketplace are the *authoring* (producer) side: they describe a
	// distributable plugin bundle and its marketplace index. Distinct from the
	// consumer Plugins/Marketplaces fields above.
	Plugin      *PluginAuthoring      `yaml:"plugin,omitempty" json:"plugin,omitempty" toml:"plugin,omitempty"`
	Marketplace *MarketplaceAuthoring `yaml:"marketplace,omitempty" json:"marketplace,omitempty" toml:"marketplace,omitempty"`

	// Placement decides whether skills and commands are generated into
	// .claude/skills (core) or shipped only through a plugin.
	Placement *PlacementConfig `yaml:"placement,omitempty" json:"placement,omitempty" toml:"placement,omitempty"`
	// Claude holds Claude Code specific output options.
	Claude *ClaudeConfig `yaml:"claude,omitempty" json:"claude,omitempty" toml:"claude,omitempty"`
	// Codex groups Codex specific options; see CodexConfig.
	Codex *CodexConfig `yaml:"codex,omitempty" json:"codex,omitempty" toml:"codex,omitempty"`

	// Hooks declares lifecycle hooks rendered into each harness's native project
	// settings (.claude/settings.json, .codex/hooks.json, .cursor/hooks.json,
	// .gemini/settings.json, .github/hooks/ai-rulez.json), outside any plugin.
	Hooks []HookGroup `yaml:"hooks,omitempty" json:"hooks,omitempty" toml:"hooks,omitempty"`
	// Guard configures the built-in hook that blocks edits to generated files.
	Guard *GuardConfig `yaml:"guard,omitempty" json:"guard,omitempty" toml:"guard,omitempty"`
	// SessionStart configures the built-in SessionStart hook that regenerates outputs in a remote session.
	SessionStart *SessionStartConfig `yaml:"session_start,omitempty" json:"session_start,omitempty" toml:"session_start,omitempty"`
	// Permissions declares allow/ask/deny rules for .claude/settings.json and, translated,
	// for every harness with a native permission surface (docs/permissions.md).
	Permissions *Permissions `yaml:"permissions,omitempty" json:"permissions,omitempty" toml:"permissions,omitempty"`

	// Runtime fields (populated during load)
	BaseDir string `yaml:"-" json:"-" toml:"-"`
	// ContentProblems lists project content paths refused by the symlink
	// policy; validate reports them as errors.
	ContentProblems []ContentProblem `yaml:"-" json:"-" toml:"-"`
	ConfigDir       string           `yaml:"-" json:"-" toml:"-"`
	ConfigDirName   string           `yaml:"-" json:"-" toml:"-"`
	// RootRel is where this project's base directory sits below the root of its
	// git work tree (forward slashes), empty for the work tree's own root and
	// outside a work tree. Generated headers use it to name a nested root's own
	// config by its path from the repository root.
	RootRel string `yaml:"-" json:"-" toml:"-"`
	// NestedRoot is set when another ai-rulez root encloses this project inside
	// the same git work tree; see HeaderConfig.Nested.
	NestedRoot bool `yaml:"-" json:"-" toml:"-"`

	// PolicyOutcome is what the organization policy clamped and reported at load
	// time (docs/policy.md); nil when no policy is in force.
	PolicyOutcome *PolicyOutcome `yaml:"-" json:"-" toml:"-"`

	// Run is the state of the generation in progress; nil outside one.
	Run        *RunState `yaml:"-" json:"-" toml:"-"`
	ConfigFile string    `yaml:"-" json:"-" toml:"-"` // Actual config filename (e.g. "config.toml")
	// Implicit marks the synthetic configuration of a project that has none (see
	// LoadImplicit): no presets, includes or config file, the content of the
	// .ai-rulez/ tree when it exists, and the project root as the authored root.
	// Only the read-only commands (validate, scan, doctor, okf validate) accept it.
	Implicit bool `yaml:"-" json:"-" toml:"-"`

	// ConfigFilePath is the absolute path of the main config file; it differs from
	// ConfigDir/ConfigFile for a configuration kept in .agents/ai-rulez.toml.
	ConfigFilePath string `yaml:"-" json:"-" toml:"-"`
	// AgentsLayout is set when the configuration was read from .agents/ai-rulez.toml,
	// which selects the "agents" source (ConfigDir is then .agents/.ai-rulez).
	AgentsLayout bool `yaml:"-" json:"-" toml:"-"`

	Content *ContentTree `yaml:"-" json:"-" toml:"-"`
	// LocalContent holds machine-local override content scanned from
	// .ai-rulez/local/ (rules + context only). It is kept strictly separate
	// from Content so it never lands in committed output; it is emitted only to
	// the per-preset ".local" root variants (CLAUDE.local.md, AGENTS.local.md,
	// ...) and is always gitignored.
	LocalContent *ContentTree `yaml:"-" json:"-" toml:"-"`
	// IncludeMemo caches fetched include and skill sources for the lifetime of
	// one generate run, so loading a second view of the same project (the
	// shared baseline used by the drift guard) never fetches twice. It is owned
	// by the includes resolver; nil means "not created yet".
	IncludeMemo any `yaml:"-" json:"-" toml:"-"`
	// ResolutionState holds what a load's include and skill fetches resolved to
	// (commit, digest, tag, problems) so `BuildLock` can write it down. It is
	// owned by the includes resolver and is per load: two loads in one process
	// keep separate records and never clobber each other. nil means "not
	// created yet".
	ResolutionState any `yaml:"-" json:"-" toml:"-"`
	// LocalOverlay is set when a config.local.* overlay was merged into this
	// configuration. Such a config is a merged view and is never written back.
	LocalOverlay *LocalOverlay `yaml:"-" json:"-" toml:"-"`
	// ActiveLayers names the config.<name>.toml layers merged into this
	// configuration, in merge order. A layered Config is a merged view and is never
	// written back as config.toml.
	ActiveLayers []string `yaml:"-" json:"-" toml:"-"`
	// LayerOrigins maps each active layer to how it was activated: "default"
	// (default_layers), "env" (AI_RULEZ_LAYERS or AI_RULEZ_PROFILE) or "option" (WithLayers).
	LayerOrigins map[string]string     `yaml:"-" json:"-" toml:"-"`
	MCPServers   map[string]*MCPServer `yaml:"-" json:"-" toml:"-"`
	// ImportedScan lists the hook commands, hook scripts and MCP definitions of
	// installed plugins that the import security scan reads besides the content
	// files. Runtime-only.
	ImportedScan []ImportedScanFile `yaml:"-" json:"-" toml:"-"`
	// PluginMCPServers names the entries of MCPServers installed plugins
	// delivered, so the organization policy can bound them. Runtime-only.
	PluginMCPServers []string    `yaml:"-" json:"-" toml:"-"`
	MCPServersRaw    []MCPServer `yaml:"mcp_servers,omitempty" json:"mcp_servers,omitempty" toml:"mcp_servers,omitempty"`

	// SourceHash is a blake3 hash over all sources that contribute to generated
	// output for the active profile (config metadata, content tree, MCP servers,
	// and the generator schema version). Computed once per generation and embedded
	// in every output file's header so that subsequent runs can detect "nothing
	// changed in sources" without re-rendering. Format: "blake3:<hex>".
	SourceHash string `yaml:"-" json:"-" toml:"-"`

	// GeneratedAt is the single timestamp a generate run stamps into every header
	// it writes, resolved once so sibling outputs (CLAUDE.md and AGENTS.md, which
	// are otherwise byte-identical) can never disagree because their renders
	// landed in different seconds. Zero means "not resolved yet"; HeaderTimestamp
	// falls back to the wall clock for callers that render a preview without a
	// generation run. Only read when [header] timestamp opts the line back in.
	GeneratedAt time.Time `yaml:"-" json:"-" toml:"-"`
	// Host is the environment, clock, process runner and logger this config was
	// loaded with (WithHost); the zero value is the real process. Generation,
	// includes and lint read them from here instead of the process.
	Host ambient.Host `yaml:"-" json:"-" toml:"-"`
	// Diag collects the warnings of the generate run this config is rendered by
	// (see internal/diag); nil outside a run, where warnings go straight to the
	// logger.
	Diag *diag.Collector `yaml:"-" json:"-" toml:"-"`
	// Registry resolves preset names to generators (WithRegistry); the Generator
	// supplies the default one when a config carries none.
	Registry *Registry `yaml:"-" json:"-" toml:"-"`
	// RulesDirs holds the rules folders custom provider specs added to this
	// project's built-in set; nil is the built-in set. Copies of a Config share it.
	RulesDirs *RulesDirSet `yaml:"-" json:"-" toml:"-"`
	// Resolve are the resolvers this config was loaded with (WithResolvers); nested
	// loads of the same project (a baseline render, a shared view) reuse them.
	Resolve Resolvers `yaml:"-" json:"-" toml:"-"`
	// LockPolicy is how this config was loaded against ai-rulez.lock
	// (WithLockPolicy); the includes resolvers and nested loads read it.
	LockPolicy LockPolicy `yaml:"-" json:"-" toml:"-"`
	// Workspace is the project tree this config was loaded from (WithWorkspace,
	// or the repository containing BaseDir); nil on a Config built by hand.
	Workspace workspace.Workspace `yaml:"-" json:"-" toml:"-"`

	// PolicyDir is the project directory the organization policy is discovered
	// from (WithPolicyDir); empty means BaseDir. A snapshot of an old revision is
	// loaded from a temporary directory with no remote, and is judged by the policy
	// of the project it is a snapshot of.
	PolicyDir string `yaml:"-" json:"-" toml:"-"`

	// DeferMalformedFrontmatter makes Validate skip the malformed-frontmatter
	// failure because the caller reports each such file as a lint finding.
	DeferMalformedFrontmatter bool `yaml:"-" json:"-" toml:"-"`

	// UserScope is set while rendering for `generate --user`: outputs are mapped
	// into the person's home config directories, so renderers leave out keys that
	// only make sense inside a project (MCP servers, plugin registration,
	// machine-local context names).
	UserScope bool `yaml:"-" json:"-" toml:"-"`

	// MCPEnvOverrides are generation-time KEY=VALUE overrides used to resolve
	// MCP env placeholders. They are intentionally not serialized.
	MCPEnvOverrides map[string]string `yaml:"-" json:"-" toml:"-"`

	// MCPEnvFiles are generation-time dotenv files used to resolve MCP env
	// placeholders. Empty means "load .env from BaseDir when present".
	MCPEnvFiles []string `yaml:"-" json:"-" toml:"-"`

	// Analysis, when non-nil, makes the rendering layer record how each output
	// was classified and what each of its sections contained. Only the `tokens`
	// command sets it; generation leaves it nil and pays nothing.
	Analysis *AnalysisCollector `yaml:"-" json:"-" toml:"-"`
	// contains filtered or unexported fields
}

Config represents the configuration format

func DecodeTOML

func DecodeTOML(data []byte, path string) (*Config, error)

DecodeTOML decodes config.toml bytes without validating them or touching the file system; path is used for error context only. `migrate v5` uses it to prove that a rewritten configuration still decodes.

func DecodeTOMLConfig

func DecodeTOMLConfig(data []byte, path string) (*Config, error)

DecodeTOMLConfig parses the bytes of a config.toml. path is used for error context only.

func LoadConfig

func LoadConfig(ctx context.Context, baseDir string, opts ...LoadOption) (*Config, error)

LoadConfig loads a configuration from the specified base directory. The baseDir should contain an .ai-rulez/ subdirectory (or, as a fallback, .config/ai-rulez/) with config.toml.

func LoadConfigFromDir

func LoadConfigFromDir(ctx context.Context, baseDir, configDirName string, opts ...LoadOption) (*Config, error)

LoadConfigFromDir loads configuration from configDirName below baseDir.

func LoadConfigFromFile

func LoadConfigFromFile(ctx context.Context, path string, opts ...LoadOption) (*Config, error)

LoadConfigFromFile loads a configuration from an exact config file path or from a config directory path. For a file path, the project base directory is the parent of the config directory (skipping a generic .config/ wrapper).

func LoadImplicit added in v5.3.0

func LoadImplicit(ctx context.Context, baseDir string, opts ...LoadOption) (*Config, error)

LoadImplicit builds the configuration of a project that has none, so the read-only commands (validate, scan, doctor, okf validate) can lint what the project holds without a config file: no presets, includes, hooks or MCP servers, the default [lint] settings, and the content tree of .ai-rulez/ (or .config/ai-rulez/) when it exists even though it has no config.toml. The files that follow a convention outside that tree (AGENTS.md, SKILL.md, .claude/, .agents/, ...) are found by the lint run, which treats them as hand-written.

Nothing is read from or written to a config file and nothing is generated; the result is marked Implicit, and the commands that need a real configuration (generate, clean, lock) never accept it.

func (*Config) AddRulesDir

func (c *Config) AddRulesDir(dir string)

AddRulesDir makes dir a rules folder of this project (see RulesDirSet.Add).

func (*Config) AdmitLinkedEntry added in v5.2.0

func (c *Config) AdmitLinkedEntry(ctx context.Context, path string) bool

AdmitLinkedEntry reports whether path, an entry met below a followed directory link while bundling content, may be bundled: a credential file or anything under .git/ is skipped with a warning.

func (c *Config) AdmitProjectSymlink(ctx context.Context, path string) (info os.FileInfo, ok bool)

AdmitProjectSymlink applies the symlink policy the project's own content is loaded under to path, a symlink met while bundling content: it is followed (the target's FileInfo is returned) only when its fully resolved target stays inside the repository root and is not a credential file. Otherwise it is refused, a warning names the path and the reason, and ok is false. Plain generate and generate --plugin share this one policy.

func (*Config) AllItems

func (c *Config) AllItems() []RoleItem

AllItems lists every rule, skill, agent, command and check of the full content tree (all domains), sorted by kind, domain and id.

func (*Config) AuthoredRoot added in v5.2.0

func (c *Config) AuthoredRoot() string

AuthoredRoot is the directory the project's own content is addressed from, and the one a content file must lie in to count as authored here rather than imported: the project root for the "agents" source (AGENTS.md and .agents sit beside the config directory), the configuration directory otherwise.

func (*Config) BazBudgetBytes added in v5.0.2

func (c *Config) BazBudgetBytes() int

BazBudgetBytes returns the byte budget of the AGENTS.md files the baz preset writes (rules.baz_budget), or 0 when the warning is off.

func (*Config) BazDedupeEnabled added in v5.0.2

func (c *Config) BazDedupeEnabled() bool

BazDedupeEnabled reports whether a path-scoped item whose globs point into several directories under one parent is written once to the shared parent AGENTS.md (rules.baz_dedupe). Off by default.

func (*Config) BazDedupeMaxCopies added in v5.0.4

func (c *Config) BazDedupeMaxCopies() int

BazDedupeMaxCopies returns rules.baz_dedupe_max, the most nested copies an item may produce before it collapses to a covering directory, or 0 when off.

func (*Config) BazDefaultRules added in v5.0.2

func (c *Config) BazDefaultRules() string

BazDefaultRules returns which items the baz preset selects for its path-scoped AGENTS.md files: BazDefaultAll (every item) unless rules.baz_default says BazDefaultNone (only items whose targets name baz).

func (*Config) BazIncludes added in v5.0.4

func (c *Config) BazIncludes(name string) bool

BazIncludes reports whether rules.baz.include names the item.

func (*Config) BazRestRules added in v5.0.5

func (c *Config) BazRestRules() string

BazRestRules returns what the baz preset does with a path-scoped item it does not nest: BazRestRoot unless rules.baz_rest says BazRestSkip.

func (*Config) BazScopedRules

func (c *Config) BazScopedRules() string

BazScopedRules returns where the baz preset puts path-scoped items: BazScopedNested unless rules.baz_scoped says BazScopedRoot.

func (*Config) BazUnscopedRules added in v5.0.4

func (c *Config) BazUnscopedRules() string

BazUnscopedRules returns what the baz preset does with a path-scoped item that cannot be nested: BazUnscopedRoot unless rules.baz_unscoped says BazUnscopedSkip.

func (*Config) CheckRoleDomains

func (c *Config) CheckRoleDomains() error

CheckRoleDomains returns the first role that lists a domain which does not exist (ErrRoleReference, AR971), for commands that publish every role and must not publish one that renders as "no content".

func (*Config) CheckSource added in v5.5.0

func (c *Config) CheckSource(kind, name, source string) error

CheckSource applies the source rules of the policy this configuration was loaded under to source; it is nil without a policy.

func (*Config) CodexSkillsDirOrDefault

func (c *Config) CodexSkillsDirOrDefault() string

CodexSkillsDirOrDefault is the slash-separated skills directory of the codex preset, relative to the output base dir.

func (*Config) Collector

func (c *Config) Collector() *diag.Collector

Collector is the config's warning collector (nil, which stands for the process default one, outside a load). Safe on a nil Config.

func (*Config) ConfigPath added in v5.2.0

func (c *Config) ConfigPath() string

ConfigPath is the main configuration file. It is not always inside ConfigDir: .agents/ai-rulez.toml sits beside the .agents/.ai-rulez state directory that ConfigDir names.

func (*Config) ContentForPreset

func (c *Config) ContentForPreset(preset string) *ContentTree

ContentForPreset returns the content tree one preset renders. Without served skills, or while a serving session renders (ServeMode), it is c.Content.

Otherwise, for a harness that can call MCP, skills whose delivery is served are left out of the tree (so no static tree lists them), and the single dynamic-skills stub is added once to the root skills unless a skill of that name already exists. A harness without MCP support gets every skill as before: a served skill is never dropped for it.

func (*Config) ContentForSharedSkills added in v5.2.0

func (c *Config) ContentForSharedSkills(readers []string) *ContentTree

ContentForSharedSkills returns the content tree of the shared .agents/skills tree, which one render serves to every preset in readers, so it follows the readers together. When all of them can call MCP it is what ContentForPreset gives each of them: served skills left out, the stub added. When none can, it is the full tree. When they differ, a harness without MCP must still find the served skills as files, so the tree keeps them and gains the stub for the harnesses that can call it.

func (*Config) Contentless added in v5.5.0

func (c *Config) Contentless() bool

Contentless reports whether the configuration has no content tree: it was read from ai-rulez.toml at the project root, so no rules, context, skills, agents, commands, checks, domains or local content exist, and the generator writes no instruction or root file (see SkipsRootFiles).

func (*Config) DeliveryConfigured

func (c *Config) DeliveryConfigured(tree *ContentTree) bool

DeliveryConfigured reports whether anything in tree or the configuration opts into dynamic delivery (served or both), at any level.

func (*Config) DeliveryFallbacks

func (c *Config) DeliveryFallbacks(tree *ContentTree) []DeliveryFallback

DeliveryFallbacks lists, per configured preset without MCP support, the skills that are served but written statically instead. Sorted by preset.

func (*Config) DeliveryStubKey added in v5.0.4

func (c *Config) DeliveryStubKey() string

DeliveryStubKey identifies a customized stub for the source hash, so editing [delivery.stub] (or the file behind body_file) rewrites the skill trees that carry the stub. It is empty while the built-in stub applies.

func (*Config) DropGuardHooks

func (c *Config) DropGuardHooks()

DropGuardHooks removes the synthesized guard group. User scope calls it once the scope is known: NewGenerator adds the group before SetUserScope runs, and the guard protects a project's generated files, not the home directory.

func (*Config) DropSessionStartHooks added in v5.5.0

func (c *Config) DropSessionStartHooks()

DropSessionStartHooks removes the synthesized session-start group; user scope calls it once the scope is known, since the hook belongs to a project's clone.

func (*Config) EffectiveDelivery

func (c *Config) EffectiveDelivery(skill ContentFile, domain string, roleOverride map[string]string) Delivery

EffectiveDelivery resolves how one skill reaches the agent. First match wins:

  1. the skill's own `delivery` frontmatter key;
  2. roleOverride, keyed by "<domain>/<skill>" for a skill of a domain, then by skill id and name, then by domain name (per-role delivery; nil means the role being rendered, see SetRoleDelivery, and none when no role applies);
  3. [domains.<domain>] delivery, for a skill owned by a domain;
  4. [skills] delivery, the global default;
  5. static.

Values that are not static/served/both are skipped, so one typo cannot change delivery silently; validation reports them (AR994).

func (*Config) EffectiveMCPServers

func (c *Config) EffectiveMCPServers() []MCPServer

EffectiveMCPServers returns the MCP servers generation uses, as written (${VAR} placeholders unresolved): the [[mcp_servers]] of the configuration in the order they were authored, then any server set only on Config.MCPServers (programmatically), sorted by name, without duplicates.

Config.MCPServers is the working copy: a render resolves placeholders in place there, so a check that must see what the author wrote reads this instead.

func (*Config) EnableGuardHooks

func (c *Config) EnableGuardHooks(binaryVersion string)

EnableGuardHooks adds the synthesized guard group to Hooks when [guard] is on. It is idempotent, so every generator built from the same Config shares one group. The group is marked HookBuiltinGuard and never written back to config.toml.

func (*Config) EnableSessionStartHooks added in v5.5.0

func (c *Config) EnableSessionStartHooks()

EnableSessionStartHooks adds the synthesized session-start group to Hooks. It is idempotent, so every generator built from the same Config shares one group, and the group is never written back to config.toml.

func (*Config) FilterTreeForRole

func (c *Config) FilterTreeForRole(content *ContentTree, flat *RoleConfig) (*ContentTree, error)

FilterTreeForRole returns the tree a role keeps: the role's domains plus the always-active ones, with each kind narrowed by the role's selectors. Context files are not role-selectable and are kept as they are.

func (*Config) FindRole

func (c *Config) FindRole(name string) (*RoleConfig, bool)

FindRole returns the role declared under name.

func (*Config) FlattenRole

func (c *Config) FlattenRole(name string) (*FlatRole, error)

FlattenRole merges a role with its parent. Merge rules:

  • domains and every exclude list are the union, parent first;
  • include lists are the union when both sides set one, otherwise whichever side sets it (an empty include means "everything the domains provide");
  • skill_mode and delivery entries are merged and the child wins per key;
  • description falls back to the parent's;
  • match hints are never inherited: they identify who holds this role.

func (*Config) GetContentForProfile

func (c *Config) GetContentForProfile(profile string) (*ContentTree, error)

GetContentForProfile returns all content for a given profile. Root-level slices contain only root content. Domains are placed in the Domains map so that preset generators can combine them via combineContentFiles / getAllDomain* helpers without duplication.

func (*Config) GetDefaultProfile

func (c *Config) GetDefaultProfile() string

GetDefaultProfile returns the default profile name

func (*Config) GetHeaderHashes

func (c *Config) GetHeaderHashes() string

GetHeaderHashes returns the configured header hash mode ("full", "content" or "none"), defaulting to "content".

func (*Config) GetHeaderStyle

func (c *Config) GetHeaderStyle() string

GetHeaderStyle returns the configured header style ("detailed", "compact", or "minimal"). Defaults to "minimal" when no header style is configured.

func (*Config) GetProfileDomains

func (c *Config) GetProfileDomains(profile string) []string

GetProfileDomains returns the list of domains for a profile. A composed value ("base,backend") returns the de-duplicated union of its elements' domains, in the order they are first named. A "*" entry expands to every domain the config knows and "!name" entries are removed; a profile made only of exclusions has no domains (Validate reports it as an error).

func (*Config) GetVersion

func (c *Config) GetVersion() string

GetVersion returns the config version

func (*Config) GuardCommand

func (c *Config) GuardCommand(binaryVersion string) string

GuardCommand is the shell command of the guard hook. It resolves the executable the way the self MCP server entry does (SelfMCPServerEntry): npx pinned to the binary's version unless the config pins another or replaces the command.

func (*Config) HasBuiltInPreset

func (c *Config) HasBuiltInPreset(name string) bool

HasBuiltInPreset reports whether the built-in preset is configured.

func (*Config) HasClaudeSettingsContent

func (c *Config) HasClaudeSettingsContent() bool

HasClaudeSettingsContent reports whether .claude/settings.json carries any key declared by [[hooks]], [permissions] or [claude.settings.managed].

func (*Config) HasGuard

func (c *Config) HasGuard() bool

HasGuard reports whether generation should add the generated-file guard hook.

func (*Config) HasLocalInputs

func (c *Config) HasLocalInputs() bool

HasLocalInputs reports whether any machine-local input (overlay file or local content) contributed to this configuration.

func (*Config) HasPluginAuthoring

func (c *Config) HasPluginAuthoring() bool

HasPluginAuthoring reports whether the configuration produces a plugin or a multi-plugin marketplace. Consumer-side [[plugins]] declarations do not count.

func (*Config) HasPluginSet added in v5.2.0

func (c *Config) HasPluginSet() bool

HasPluginSet reports whether the root generates a set of plugins rather than one plugin bundle: monorepo members, domain plugins, plugin definitions or marketplace definitions.

func (*Config) HasProfile

func (c *Config) HasProfile(profile string) bool

HasProfile returns true if the profile exists. Every element of a composed value must exist; a value with no elements at all exists only if a profile was literally defined under that name.

func (*Config) HasSelfServer

func (c *Config) HasSelfServer() bool

HasSelfServer reports whether generation should add ai-rulez's own MCP server. It is on by default: only an explicit self_server = false (or --no-self-mcp) turns it off.

func (*Config) HasSessionStart added in v5.5.0

func (c *Config) HasSessionStart() bool

HasSessionStart reports whether generation should add the session-start hook: it is on by default for a project that generates for Claude Code, unless a [[hooks]] group of the user already runs `ai-rulez session-start` (the user's wins).

func (*Config) HasSettingsHooks

func (c *Config) HasSettingsHooks() bool

HasSettingsHooks reports whether top-level [[hooks]] are declared for the run. A [[scopes]] subdirectory renders none (project-wide hooks do not belong in it), so the built-in hooks that every Claude Code project gets do not create an empty settings file there.

func (*Config) HasSkipOutputs added in v5.5.0

func (c *Config) HasSkipOutputs() bool

HasSkipOutputs reports whether skip_outputs is set. Safe on a nil receiver.

func (*Config) HeaderMCPCommand added in v5.0.4

func (c *Config) HeaderMCPCommand() (cmd string, show bool)

HeaderMCPCommand returns the command the header tells agents to run as the ai-rulez MCP server, derived from the same [mcp] settings that write the .mcp.json entry (self_server_command, self_server_version, and the --serve-skills flag when skills are served). show is false when the header must not mention a server: [header] mcp_line = false, or [mcp] self_server = false, where no ai-rulez server is configured to point at.

func (*Config) HeaderTimestamp

func (c *Config) HeaderTimestamp() time.Time

HeaderTimestamp returns the timestamp every header of the current run carries. Generation resolves it once into GeneratedAt; the wall-clock fallback covers callers that render a header outside a generation run, such as a preview.

func (*Config) InRulesDir

func (c *Config) InRulesDir(relPath string) bool

InRulesDir reports whether relPath is a file inside a rules folder of this project.

func (*Config) IndexCacheMode added in v5.5.0

func (c *Config) IndexCacheMode() string

IndexCacheMode is the validated [mcp] index_cache mode, user when it is unset or invalid (Validate reports the latter).

func (*Config) IsCompact

func (c *Config) IsCompact() bool

IsCompact reports whether compact rendering is enabled. When true, presets omit per-rule "**Priority:**" annotations from inline rule sections to reduce output size. Defaults to false (full output).

func (*Config) LLMsTxtDir

func (c *Config) LLMsTxtDir() string

LLMsTxtDir returns the output directory, relative to the project root ("." for the root).

func (*Config) LLMsTxtEnabled

func (c *Config) LLMsTxtEnabled() bool

LLMsTxtEnabled reports whether the llms-txt preset is configured.

func (*Config) LLMsTxtSettings

func (c *Config) LLMsTxtSettings() LLMsTxtConfig

LLMsTxtSettings returns the [llms_txt] table, or an empty one.

func (*Config) LintOnly added in v5.5.0

func (c *Config) LintOnly() bool

LintOnly reports whether the configuration declares no preset and authors no plugin: ai-rulez is used only to validate, scan and run [[lint.external]], and generate has nothing to render.

func (*Config) LocalDir added in v5.5.0

func (c *Config) LocalDir() string

LocalDir is the directory for machine-local state (usage logs, the role ledger): <ConfigDir>/local, or in the root layout a hidden directory matched by the managed ignore patterns, so it cannot be mistaken for a folder of the project.

func (*Config) LocalManifestName added in v5.5.0

func (c *Config) LocalManifestName() string

LocalManifestName is the file name of the gitignored manifest of machine-local outputs.

func (*Config) LockEnforceOptedOut

func (c *Config) LockEnforceOptedOut() bool

LockEnforceOptedOut reports an explicit `enforce = false`. `lock` records the pins enforcement needs (served skills) unless it is set, because the lock it writes turns enforcement on.

func (*Config) LockEnforced

func (c *Config) LockEnforced() bool

LockEnforced reports whether the lock is enforced: [lock] enforce when set, otherwise whether ai-rulez.lock exists in the configuration directory.

func (*Config) LockIncludeOutputs

func (c *Config) LockIncludeOutputs() bool

LockIncludeOutputs reports whether generated outputs are pinned: true unless include_outputs is false or the scope is "skills".

func (*Config) LockMinReleaseAge

func (c *Config) LockMinReleaseAge() string

LockMinReleaseAge is the global default minimum release age ("" when unset).

func (*Config) LockMinReleaseAgeSource

func (c *Config) LockMinReleaseAgeSource() string

LockMinReleaseAgeSource is the configured release-time source, "auto" by default.

func (*Config) LockScope

func (c *Config) LockScope() string

LockScope returns the configured scope, "all" by default.

func (*Config) LockVerifyTags

func (c *Config) LockVerifyTags() bool

LockVerifyTags reports whether [lock] verify_tags is on.

func (*Config) Log

func (c *Config) Log() logger.Logger

Log is the logger of this config's host (the CLI's when none was given). It is safe on a nil Config.

func (*Config) LstatExisting

func (c *Config) LstatExisting(path string) (fs.FileInfo, error)

LstatExisting is os.Lstat of an existing output, read from the project's workspace.

func (*Config) MalformedFrontmatterPaths

func (c *Config) MalformedFrontmatterPaths() []string

MalformedFrontmatterPaths lists the content files whose frontmatter block could not be parsed.

func (*Config) ManagedClaudeSettings

func (c *Config) ManagedClaudeSettings() *ManagedSettings

ManagedClaudeSettings returns the [claude.settings.managed] block, or nil.

func (*Config) ManagesClaudeSettings

func (c *Config) ManagesClaudeSettings() bool

ManagesClaudeSettings reports whether generate owns plugin keys in .claude/settings.json.

func (*Config) ManifestName added in v5.5.0

func (c *Config) ManifestName() string

ManifestName is the file name of the generated manifest, which lists every path a run wrote. It lives in ConfigDir.

func (*Config) MarketplaceTargets added in v5.2.0

func (c *Config) MarketplaceTargets() []MarketplaceTarget

MarketplaceTargets returns the indexes the root writes: the single [marketplace] block, else each [[marketplaces_authoring]] entry, else (for a root with only [[plugins_authoring]]) one index named after the project that lists them all.

func (*Config) NestedHeaderMode added in v5.0.4

func (c *Config) NestedHeaderMode() string

NestedHeaderMode returns how this root's header differs from the outer root's: "inherit" (the full header of the configured style), "compact" (one line) or "omit". A root that no other root encloses always inherits. For a nested root the default is compact, except that a custom [header] text is kept unless [header] nested is set explicitly.

func (*Config) OKFDir

func (c *Config) OKFDir() string

OKFDir returns the bundle directory, relative to the project root.

func (*Config) OKFEnabled

func (c *Config) OKFEnabled() bool

OKFEnabled reports whether the okf preset is configured.

func (*Config) OKFInclude

func (c *Config) OKFInclude() []string

OKFInclude returns the configured kinds (empty for all).

func (*Config) OKFIndexStyle

func (c *Config) OKFIndexStyle() string

OKFIndexStyle returns the configured index.md style; the default is "body".

func (*Config) OmitsAgentField

func (c *Config) OmitsAgentField(field string) bool

OmitsAgentField reports whether the named agent frontmatter field is configured to be omitted.

func (*Config) OmitsClaudeShim added in v5.0.2

func (c *Config) OmitsClaudeShim() bool

OmitsClaudeShim reports whether [claude] claude_md = "none" turns off the CLAUDE.md shim, so the claude preset relies on the shared AGENTS.md alone. Safe on a nil receiver.

func (*Config) OnceKey

func (c *Config) OnceKey(key string) bool

OnceKey reports whether this is the first call for key in the life of this config's collector, which a second render by the same command (a clean plan and the clean) does not reset. Safe on a nil Config.

func (*Config) OncePerRun added in v5.5.0

func (c *Config) OncePerRun(key string) bool

OncePerRun reports whether this is the first call for key in this process for the project at BaseDir. A command loads the project several times (lock, generate, validate), each load with its own collector, so what the project says about itself on every load is said once through this instead. Safe on a nil Config.

func (*Config) OpenExisting

func (c *Config) OpenExisting(path string) (fs.File, error)

OpenExisting is os.Open of an existing output, read from the project's workspace.

func (*Config) PluginDefinitions added in v5.2.0

func (c *Config) PluginDefinitions() []*MarketplacePlugin

PluginDefinitions returns every plugin definition of the root, in declaration order: the [[marketplace.plugins]] entries, then the [[plugins_authoring]] entries (including the ones read from .ai-rulez/plugins/<name>.toml). It is the one place that knows both spellings.

func (*Config) PluginSelectorProblems added in v5.2.0

func (c *Config) PluginSelectorProblems() []PluginSelectorProblem

PluginSelectorProblems lists the skills, commands, agents and domains selectors of the plugin definitions that match nothing in the loaded content, sorted by plugin and field. Builtin content is never bundled, so it does not count.

func (*Config) PluginTrust added in v5.5.0

func (c *Config) PluginTrust(p *InstalledPluginConfig) string

PluginTrust is the scan level of an installed plugin: its own, else the one of the marketplace it is installed from, else "" (the default).

func (*Config) Policy

func (c *Config) Policy() PolicyEnforcer

Policy is the enforcer the configuration was loaded under (nil: none).

func (*Config) PolicyLocks

func (c *Config) PolicyLocks(feature string) bool

PolicyLocks reports whether the policy this configuration was loaded under forbids network use of the feature, for the project this configuration is.

func (*Config) PolicyProjectDir

func (c *Config) PolicyProjectDir() string

PolicyProjectDir is the directory the organization policy is discovered from: PolicyDir, else BaseDir.

func (*Config) ProfileBuiltinRefs

func (c *Config) ProfileBuiltinRefs() []string

ProfileBuiltinRefs returns every builtin named by a "builtin:<name>" element in any profile's domain list, de-duplicated and in deterministic order. These packs are loaded scoped to the profiles that name them rather than globally.

func (*Config) ReadExisting

func (c *Config) ReadExisting(path string) ([]byte, error)

ReadExisting reads a file that may already exist where the project's outputs go (a settings document, the previous manifest, a hand-edited output) from the project's workspace, so planning against a workspace in memory or in a commit sees that workspace's outputs, not the disk of this process.

func (*Config) ReadsSharedAgentsMD

func (c *Config) ReadsSharedAgentsMD(preset string) bool

ReadsSharedAgentsMD reports whether agents_md is on and the built-in preset relies on the shared AGENTS.md in place of its own root instructions file.

func (*Config) RelativeDirectoryMarketplace

func (c *Config) RelativeDirectoryMarketplace() (path string, ok bool)

RelativeDirectoryMarketplace reports whether the managed extraKnownMarketplaces entry points at a directory given as a relative path, which is the default when no marketplace_source is set. It returns the path.

func (*Config) ReloadOptions

func (c *Config) ReloadOptions(extra ...LoadOption) []LoadOption

ReloadOptions returns the options that load this project again the way c was loaded: through the same workspace, host (environment, clock, runner and logger), registry, resolvers, include fetch memo, organization policy and its directory, warning collector and lock policy. extra come last and win. A nested load of the same project (the shared view without the machine-local overlay, a baseline render) uses it, so it cannot fall back to the real disk, the process environment or the real git while the first load did not.

func (*Config) ResolveImprove

func (c *Config) ResolveImprove(trustRepo bool, getenv func(string) string) (ImproveResolution, error)

ResolveImprove merges [improve] from the repository and the user config file. The user file wins key by key; a repository optimizer and env_pass are used only when trustRepo is set (a hostile repository must not choose a command that runs on your machine or the variables it receives), and a repository gate key looser than the default is dropped unless trustRepo is set (LoosenedRepoKeys). getenv may be nil (os.Getenv).

func (*Config) ResolveJudge added in v5.4.0

func (c *Config) ResolveJudge(getenv func(string) string) (JudgeResolution, error)

ResolveJudge layers the repository [judge] table and the user config file's. A repository (and its local overlay) cannot choose where content goes or what a run spends: models, spend caps, rate limits, consent and the prompt-override switch are taken from the user config only, and the keys the repository set are reported. getenv may be nil (os.Getenv). c may be nil: a project with no configuration still has a user table.

func (*Config) ResolveLLM

func (c *Config) ResolveLLM(getenv func(string) string) (LLMResolution, error)

ResolveLLM layers the repository llm table (config plus local overlay), the user config file and the AI_RULEZ_LLM_* environment, applying the trust rule: allow_network, base_url, api_key_env and price overrides are honored only from the user config file or the environment. getenv may be nil (os.Getenv).

func (*Config) ResolveReview

func (c *Config) ResolveReview(getenv func(string) string) (ReviewResolution, error)

ResolveReview reads the user-scope part of [review]. A repository config (and its local overlay) cannot set allowed_hosts: it would let a hostile repository extend the list of places a prompt may go, so the key is ignored and reported. getenv may be nil (os.Getenv).

func (*Config) ResolveRole

func (c *Config) ResolveRole(name string) (*ResolvedRole, error)

ResolveRole computes the effective item set of a role over the loaded content. A role that names a domain which does not exist is an error (ErrRoleReference, AR971): resolving it anyway would render the role as "no content", the wrong direction for a role that is meant to restrict access. ResolveRole resolves a role into its effective item set. name may be a comma-separated composition (see ResolveRoles).

func (*Config) ResolveRoles added in v5.0.1

func (c *Config) ResolveRoles(names []string) (*ResolvedRole, error)

ResolveRoles composes one or more roles into a single effective role: the union of what each grants. A single name resolves exactly like ResolveRole.

func (*Config) ResolvedLLM

func (c *Config) ResolvedLLM() (llm.Config, error)

ResolvedLLM returns the effective llm table (zero value when absent) with the trust rule and the AI_RULEZ_LLM_* environment overrides applied.

func (*Config) RoleActive

func (c *Config) RoleActive() bool

RoleActive reports whether this Config is rendering a role (SetRoleDelivery was called). Outputs that belong to the whole project, such as the committed OKF bundle, are left alone while a role renders.

func (*Config) RoleDeliveryOverride

func (c *Config) RoleDeliveryOverride(role *RoleConfig) map[string]string

RoleDeliveryOverride resolves a (flattened) role's delivery selectors against the content tree and returns the override EffectiveDelivery takes: one entry per skill the role sets a delivery for, keyed by "<domain>/<id>" for a skill of a domain and by id for a root skill.

func (*Config) RoleManifestEnabled

func (c *Config) RoleManifestEnabled() bool

RoleManifestEnabled reports whether generate writes roles.json.

func (*Config) RoleNames

func (c *Config) RoleNames() []string

RoleNames returns the declared role names, sorted.

func (*Config) RoleProblems

func (c *Config) RoleProblems() []RoleProblem

RoleProblems checks every role against the loaded content: references that name nothing, broken inheritance, and skills a kept item depends on that the role leaves out.

func (*Config) RoleSkillModeFallback

func (c *Config) RoleSkillModeFallback() string

RoleSkillModeFallback returns the configured fallback, "drop" when unset.

func (*Config) RolesServeSkills

func (c *Config) RolesServeSkills() bool

RolesServeSkills reports whether some role sets a delivery other than static, so the project can serve skills even when no skill, domain or global default does.

func (*Config) RulesDirRemainder

func (c *Config) RulesDirRemainder(relPath string) (rest string, ok bool)

RulesDirRemainder is RulesDirSet.Remainder for this project's folders.

func (*Config) RulesModeExplicitFor

func (c *Config) RulesModeExplicitFor(preset string) bool

RulesModeExplicitFor reports whether the preset's rules mode is set explicitly through rules.mode_by_preset.

func (*Config) RulesModeFor

func (c *Config) RulesModeFor(preset string) string

RulesModeFor returns the rules output mode for a preset: rules.mode_by_preset[preset] > rules.mode > the built-in default.

func (*Config) SelectContentForDomains

func (c *Config) SelectContentForDomains(content *ContentTree, domains []string) (*ContentTree, error)

SelectContentForDomains applies a domain list to a content tree the way a profile does: root content, globally active builtins, every included domain and the listed domains.

func (*Config) SelectContentForProfile

func (c *Config) SelectContentForProfile(content *ContentTree, profile string) (*ContentTree, error)

SelectContentForProfile applies a profile's domain selection to a content tree: the root content plus the domains the profile names, the globally active builtins and every domain that came from an include. It is the single implementation of that selection, used for the shared tree and for the machine-local one (.ai-rulez/local/) alike.

func (*Config) SelfMCPServerEntry

func (c *Config) SelfMCPServerEntry() map[string]any

SelfMCPServerEntry builds the .mcp.json entry for ai-rulez's own MCP server. It runs ai-rulez@latest by default, so a project picks up new releases without regenerating; pin a version with mcp.self_server_version or replace the launch with mcp.self_server_command. An empty or "dev" version resolves to "latest".

When the project serves skills the entry runs `mcp --serve-skills`, the merged server that serves the skills and the authoring tools from one process, so a project needs one MCP entry rather than the self entry plus a separate skills server.

func (*Config) ServesSkills

func (c *Config) ServesSkills(tree *ContentTree) bool

ServesSkills reports whether a skills server has anything to serve: a skill of tree whose delivery is served or both, or a [[skill_sources]] entry (whose skills are served, never written). The dynamic-skills stub, and the checks that an agent is told to call find_skill, apply exactly then.

func (*Config) SessionStartCommand added in v5.5.0

func (c *Config) SessionStartCommand() string

SessionStartCommand is the shell command of the session-start hook. The script (no single quotes in it) tries an installed ai-rulez, then npx, then uvx (the latter two on the latest release unless pinned, and from a neutral directory); an attempt that fails to run falls through to the next, and when none runs it prints one line and exits 0, so it never fails a session. With when = "remote" it returns at once unless CLAUDE_CODE_REMOTE is set, before npx has a chance to download anything on a developer's machine.

func (*Config) SessionStartContext added in v5.5.0

func (c *Config) SessionStartContext(failure string) string

SessionStartContext is the text the hook injects into the agent's context. Every part is derived from the configuration: the generated files and where their sources are, the MCP server (only when it is enabled) and the served-skills section (the one [mcp] skills_instructions writes into the instruction files). failure, when not empty, is the one-line reason the generate step failed. The result is deterministic, free of control characters and at most SessionStartContextLimit bytes.

func (*Config) SessionStartLaunchers added in v5.5.0

func (c *Config) SessionStartLaunchers() []SessionStartLauncher

SessionStartLaunchers lists the launchers the hook tries, in order: an installed ai-rulez, then npx, then uvx, the last two on the latest published release unless [session_start] version pins one. A configured command is the only launcher.

func (*Config) SetRoleDelivery

func (c *Config) SetRoleDelivery(override map[string]string)

SetRoleDelivery makes the per-skill delivery of a role the one every later EffectiveDelivery call without an explicit override uses, so a role render, a token report and the skills server all see the same split. It changes this Config: call it on a copy when the Config is shared. A nil override still marks a role as active (it sets no delivery of its own).

func (*Config) SetSelfServer

func (c *Config) SetSelfServer(on bool)

SetSelfServer forces the self server on or off, overriding the default and any configured value. The generate --no-self-mcp flag calls it.

func (*Config) ShouldUpdateGitignore

func (c *Config) ShouldUpdateGitignore() bool

ShouldUpdateGitignore returns whether .gitignore should be updated

func (*Config) ShowHeaderTimestamp

func (c *Config) ShowHeaderTimestamp() bool

ShowHeaderTimestamp reports whether generated headers include the "Generated:" line. Defaults to false; see HeaderConfig.Timestamp.

func (*Config) SkillDeliveries

func (c *Config) SkillDeliveries(tree *ContentTree, roleOverride map[string]string) []PlannedSkill

SkillDeliveries lists every skill of tree with its effective delivery, root skills first then domains in name order. roleOverride is passed to EffectiveDelivery.

func (*Config) SkillsInstructions added in v5.0.4

func (c *Config) SkillsInstructions() (text string, show bool)

SkillsInstructions returns the section that tells an agent how to load served skills, and whether to write it into the instruction files. Harnesses that do not list the dynamic-skills stub (Codex, Cursor, a subagent with a tool allowlist) get no other pointer to skills that exist only over MCP.

It applies when skills are served, a configured preset can call MCP, and [mcp] skills_instructions is not false. The text is [mcp] skills_instructions_text with {{server}} and {{skills_dir}} replaced, or a short default naming find_skill, load_skill, the server and the on-disk fallback. The result does not depend on the preset: a root file several presets write (AGENTS.md) must render identically whichever writes it last.

func (*Config) SkillsInstructionsFor added in v5.0.4

func (c *Config) SkillsInstructionsFor(preset string) (text string, show bool)

SkillsInstructionsFor is SkillsInstructions for a root file that only preset writes: a preset whose harness cannot call MCP gets no section, because the served skills are written to its skills folder instead. An empty preset names a root file several presets share.

func (*Config) SkipsAgentsMD added in v5.5.0

func (c *Config) SkipsAgentsMD() bool

SkipsAgentsMD reports whether no shared AGENTS.md is written on purpose, through "instructions" or "agents_md". Safe on a nil receiver.

func (*Config) SkipsOutput added in v5.5.0

func (c *Config) SkipsOutput(kind string) bool

SkipsOutput reports whether skip_outputs lists kind. Safe on a nil receiver.

func (*Config) SkipsRootFiles added in v5.5.0

func (c *Config) SkipsRootFiles() bool

SkipsRootFiles reports whether the generator must leave the instruction and root files (AGENTS.md, CLAUDE.md, rule files, ...) alone because the project keeps them by hand. It is the one place that decides this, so that it can be unified with a general skip_outputs setting later.

func (*Config) SourceDir added in v5.2.0

func (c *Config) SourceDir() string

SourceDir is the directory the authored content tree (rules, context, skills, agents, commands, checks, domains) is read from: .agents for the "agents" source, the config directory otherwise.

func (*Config) SourceSkillBlindPresets

func (c *Config) SourceSkillBlindPresets() []string

SourceSkillBlindPresets lists the configured presets without MCP support when the project has [[skill_sources]]. Source skills are served only, never written to a harness's skill tree, so these harnesses cannot see them. Sorted.

func (*Config) StatExisting

func (c *Config) StatExisting(path string) (fs.FileInfo, error)

StatExisting is os.Stat of an existing output, read from the project's workspace.

func (*Config) UnknownProfileNames

func (c *Config) UnknownProfileNames(profile string) []string

UnknownProfileNames returns the elements of a profile value that are not defined in the config, in the order they were written. Used to name the bad element in an error rather than echoing the whole composed value, which leaves the reader to work out which part was wrong.

func (*Config) UserHooks

func (c *Config) UserHooks() []HookGroup

UserHooks returns the hook groups the user wrote, without synthesized ones.

func (*Config) UsesAgentsSource added in v5.2.0

func (c *Config) UsesAgentsSource() bool

UsesAgentsSource reports whether the project's content is read from AGENTS.md and .agents rather than from the .ai-rulez content tree.

func (*Config) Validate

func (c *Config) Validate() error

Validate validates a configuration

func (*Config) View

func (c *Config) View() workspace.View

View reads the workspace the configuration was loaded from. A Config built without one (a literal in a test) reads the real directory it names.

func (*Config) ViewFor

func (c *Config) ViewFor(dir string) workspace.View

ViewFor returns a view that can read dir: the config's workspace when dir is inside it, otherwise the real directory dir when the workspace is backed by the real file system. Included content that lives in a cache outside the project is read that way.

func (*Config) Warn

func (c *Config) Warn(msg string, args ...any)

Warn logs a warning through the host logger. The advice of a generate run goes through Diag instead, which a command can silence or de-duplicate; this is for what must always be shown. Safe on a nil Config.

func (*Config) WarnDeliveryFallbacks

func (c *Config) WarnDeliveryFallbacks()

WarnDeliveryFallbacks logs, once per project and preset, that a preset without MCP support keeps served skills as static files. Nothing is dropped silently.

func (*Config) WarnOnce

func (c *Config) WarnOnce(key, msg string, args ...any)

WarnOnce logs msg once through the host logger, whatever sink the run's advice is sent to. Safe on a nil Config.

type ContentEnforcer

type ContentEnforcer interface {
	EnforceContent(ctx context.Context, cfg *Config) []PolicyViolation
}

ContentEnforcer is a PolicyEnforcer that also bounds the content includes and installed skills deliver. Enforce runs before anything is fetched, so what that content declares (hooks and MCP servers in its frontmatter) is judged once it is loaded. The violations it returns are added to the outcome.

type ContentFile

type ContentFile struct {
	Name     string    `yaml:"name" json:"name"`
	Path     string    `yaml:"path" json:"path"`
	Content  string    `yaml:"content" json:"content"`
	Metadata *Metadata `yaml:"metadata,omitempty" json:"metadata,omitempty"`
	// Profiles scopes this file to the named profiles (used by installed
	// skills). Empty means every profile. Runtime-only; not serialized.
	Profiles []string `yaml:"-" json:"-"`
	// Domain and Trust carry an installed skill's entry scope onto every file a
	// multi-skill entry resolved, so generation and the lint scan keep it
	// per-item instead of looking it up by name. Runtime-only; not serialized.
	Domain string `yaml:"-" json:"-"`
	Trust  string `yaml:"-" json:"-"`

	// ID is the output id of a skill when it is not the name of the directory
	// holding SKILL.md: an installed skill entry's name_prefix prefixes the id as
	// well as the name. Empty means the directory name. Runtime-only.
	ID string `yaml:"-" json:"-"`

	// Resources holds skill supporting files (references/, scripts/, assets/)
	// loaded alongside SKILL.md. Always empty for non-skill content.
	Resources []SkillResource `yaml:"-" json:"-"`

	// Verifiers holds the verifier declaration files an installed skill ships in
	// its verifiers/ directory. They are data: see ImportedVerifierFile.
	Verifiers []ImportedVerifierFile `yaml:"-" json:"-"`

	// MalformedFrontmatter is true when the source file contained a delimited
	// YAML frontmatter block (---...---) but its content was unparseable.
	// It is set during loading and used by Config.Validate to fail fast.
	MalformedFrontmatter bool `yaml:"-" json:"-"`
}

ContentFile represents a single content file with optional frontmatter

func ContentFileFromText added in v5.3.0

func ContentFileFromText(path, text string) ContentFile

ContentFileFromText builds the ContentFile of text that belongs to path: the name from the file name, the frontmatter split from the body. It reads nothing, so a caller can parse a file it already holds (or bytes a generate run would write) the way the loader parses the files of a content tree. A malformed frontmatter is recorded in the result and never logged.

func DynamicSkillsStub

func DynamicSkillsStub(server string) ContentFile

DynamicSkillsStub builds the built-in stub skill; server names the MCP server that serves the skills. A project's own stub, with [delivery.stub] applied, is Config.dynamicSkillsStub.

func FilterContentFilesByProfile

func FilterContentFilesByProfile(files []ContentFile, profile string) []ContentFile

FilterContentFilesByProfile keeps the files active for a profile: those with no profile restriction, plus those whose Profiles include one of the profile value's elements.

func LoadSkillDir added in v5.3.0

func LoadSkillDir(ctx context.Context, root, skillDir string) (skill ContentFile, ok bool)

LoadSkillDir loads the skill whose SKILL.md lies in skillDir, with its resources, the way a content tree's skills/ directory is scanned. root is the project directory: a symlink inside it that stays inside it is followed, any other is refused. ok is false when skillDir holds no readable SKILL.md.

func (*ContentFile) GetFileExtension

func (f *ContentFile) GetFileExtension() string

GetFileExtension returns the file extension for the content file

func (*ContentFile) IsMarkdown

func (f *ContentFile) IsMarkdown() bool

IsMarkdown returns true if the content file is markdown

type ContentProblem

type ContentProblem struct {
	Path   string
	Reason string
}

ContentProblem is a project content path the loader refused to read.

type ContentTree

type ContentTree struct {
	Rules    []ContentFile      `yaml:"rules,omitempty" json:"rules,omitempty"`
	Context  []ContentFile      `yaml:"context,omitempty" json:"context,omitempty"`
	Skills   []ContentFile      `yaml:"skills,omitempty" json:"skills,omitempty"`
	Agents   []ContentFile      `yaml:"agents,omitempty" json:"agents,omitempty"`
	Commands []ContentFile      `yaml:"commands,omitempty" json:"commands,omitempty"`
	Checks   []ContentFile      `yaml:"checks,omitempty" json:"checks,omitempty"`
	Domains  map[string]*Domain `yaml:"domains,omitempty" json:"domains,omitempty"`
	// ImportedVerifiers are the verifier declaration files of includes; the
	// project's own are read from its configuration directory.
	ImportedVerifiers []ImportedVerifierFile `yaml:"-" json:"-"`
}

ContentTree represents the scanned content from .ai-rulez/ directory

func ScanContentTree

func ScanContentTree(configDir string) (*ContentTree, error)

ScanContentTree scans all content directories and returns a populated ContentTree. Root content goes into the top-level slices; domain content goes only into the Domains map. This keeps the two layers separate so callers (e.g. include sources) can merge without duplication.

func ScanContentTreeContext

func ScanContentTreeContext(ctx context.Context, configDir string) (*ContentTree, error)

ScanContentTreeContext is ScanContentTree whose git questions (which files a work tree ignores) run through the runner ctx carries (runner.WithContext); without one it runs real git.

func ScanContentTreeIn

func ScanContentTreeIn(ctx context.Context, v workspace.View, configDir string) (*ContentTree, error)

ScanContentTreeIn is ScanContentTreeContext reading through v, for content that lives inside a workspace (a local include) rather than in a directory of the real file system. configDir is an absolute path below v's root. Symlinks are never followed, as for any included content.

func ScanContentTreeWith

func ScanContentTreeWith(configDir string, bundleExclude []string) (*ContentTree, error)

ScanContentTreeWith is ScanContentTree with extra bundle_exclude patterns applied to the resources of every skill and command.

func ScanLocalContentTree

func ScanLocalContentTree(configDir string) (*ContentTree, error)

ScanLocalContentTree scans machine-local override content from <configDir>/local/, which mirrors the shared layout: rules, context, skills, agents, commands and domains. The tree is kept separate from the committed content tree so local content is only ever written to gitignored outputs.

func ScanLocalContentTreeWith

func ScanLocalContentTreeWith(configDir string, bundleExclude []string) (*ContentTree, error)

ScanLocalContentTreeWith is ScanLocalContentTree with extra bundle_exclude patterns.

func (*ContentTree) GetAgentsForDomains

func (t *ContentTree) GetAgentsForDomains(domains []string) []ContentFile

GetAgentsForDomains returns agents for specified domains (including root)

func (*ContentTree) GetAllContentFiles

func (t *ContentTree) GetAllContentFiles() []ContentFile

GetAllContentFiles returns all content files from the tree

func (*ContentTree) GetChecksForDomains

func (t *ContentTree) GetChecksForDomains(domains []string) []ContentFile

GetChecksForDomains returns checks for specified domains (including root)

func (*ContentTree) GetCommandsForDomains

func (t *ContentTree) GetCommandsForDomains(domains []string) []ContentFile

GetCommandsForDomains returns commands for specified domains (including root)

func (*ContentTree) GetContextForDomains

func (t *ContentTree) GetContextForDomains(domains []string) []ContentFile

GetContextForDomains returns context files for specified domains (including root)

func (*ContentTree) GetRulesForDomains

func (t *ContentTree) GetRulesForDomains(domains []string) []ContentFile

GetRulesForDomains returns rules for specified domains (including root)

func (*ContentTree) GetSkillsForDomains

func (t *ContentTree) GetSkillsForDomains(domains []string) []ContentFile

GetSkillsForDomains returns skills for specified domains (including root)

func (*ContentTree) IsEmpty

func (t *ContentTree) IsEmpty() bool

IsEmpty reports whether the tree carries no content of any kind.

type CursorExtras

type CursorExtras struct {
	// Marketplace also writes .cursor-plugin/marketplace.json for a
	// single-plugin repository.
	Marketplace bool `yaml:"marketplace,omitempty" json:"marketplace,omitempty" toml:"marketplace,omitempty"`
}

CursorExtras holds Cursor-plugin-specific switches.

type DefaultsConfig

type DefaultsConfig struct {
	// Effort is the default reasoning effort applied across providers that support it.
	// Accepted values: low, medium, high, xhigh, max, inherit. Empty string means "not set".
	// Each preset maps the value to its own vocabulary (see internal/generator/presets/effort.go).
	Effort string `yaml:"effort,omitempty" json:"effort,omitempty" toml:"effort,omitempty"`

	// EffortByPreset overrides Effort for specific presets. Keys are preset names (e.g. "codex",
	// "claude", "devin"). Per-agent frontmatter still wins over this map where the preset
	// supports per-agent effort.
	EffortByPreset map[string]string `yaml:"effort_by_preset,omitempty" json:"effort_by_preset,omitempty" toml:"effort_by_preset,omitempty"`

	// ModelByPreset overrides the agent `model` per preset. Keys are preset names (e.g.
	// "claude", "copilot", "cursor"). Per-agent `<preset>_model` frontmatter still wins
	// over this map; the legacy `model` field is the lowest-priority fallback. Presets
	// that do not emit a per-agent model frontmatter ignore entries for their preset.
	ModelByPreset map[string]string `yaml:"model_by_preset,omitempty" json:"model_by_preset,omitempty" toml:"model_by_preset,omitempty"`

	// OmitAgentFields lists agent frontmatter fields to suppress for every preset,
	// so an agent stays loadable in a tool where a field would be invalid (an
	// unconfigured model/provider, a tool name the tool does not recognize, ...).
	// Recognized values: "model", "effort", "tools", "description". Omitted fields
	// are simply not written; nothing else changes.
	OmitAgentFields []string `yaml:"omit_agent_fields,omitempty" json:"omit_agent_fields,omitempty" toml:"omit_agent_fields,omitempty"` //nolint:tagliatelle
}

DefaultsConfig represents top-level defaults that propagate into generated outputs when individual content files do not override them.

type Delivery

type Delivery string

Delivery says how a skill reaches the agent.

const (
	// DeliveryStatic writes the skill into every harness's skill tree (default).
	DeliveryStatic Delivery = "static"
	// DeliveryServed leaves the skill out of the static trees and serves it over
	// MCP (`ai-rulez mcp --serve-skills`) on demand.
	DeliveryServed Delivery = "served"
	// DeliveryBoth writes the skill statically and also serves it.
	DeliveryBoth Delivery = "both"
)

Delivery values.

func ParseDelivery

func ParseDelivery(v string) (Delivery, bool)

ParseDelivery parses a delivery value; ok is false for anything unknown. An empty string is not a value (it means "not set") and reports ok=false.

type DeliveryConfig added in v5.0.4

type DeliveryConfig struct {
	// Stub customizes the generated dynamic-skills stub skill.
	Stub *StubConfig `yaml:"stub,omitempty" json:"stub,omitempty" toml:"stub,omitempty"`
}

DeliveryConfig is the [delivery] table: how served skills are presented to the agent.

type DeliveryFallback

type DeliveryFallback struct {
	Preset string
	Skills []string
}

DeliveryFallback describes served skills a preset keeps static because its harness has no MCP support.

type DirLocker

type DirLocker interface {
	LocksIn(feature, dir string) bool
}

DirLocker is a PolicyEnforcer that can answer Locks for one project, whose organization policy it discovers from the repository's owner.

type Domain

type Domain struct {
	Name          string        `yaml:"name" json:"name"`
	Rules         []ContentFile `yaml:"rules,omitempty" json:"rules,omitempty"`
	Context       []ContentFile `yaml:"context,omitempty" json:"context,omitempty"`
	Skills        []ContentFile `yaml:"skills,omitempty" json:"skills,omitempty"`
	Agents        []ContentFile `yaml:"agents,omitempty" json:"agents,omitempty"`
	Commands      []ContentFile `yaml:"commands,omitempty" json:"commands,omitempty"`
	Checks        []ContentFile `yaml:"checks,omitempty" json:"checks,omitempty"`
	Builtin       bool          `yaml:"-" json:"-"` // true if loaded from builtins
	BuiltinScoped bool          `yaml:"-" json:"-"` // true if a builtin loaded only for the profiles that name it (builtin:<name>)
	FromInclude   bool          `yaml:"-" json:"-"` // true if loaded from an external include
}

Domain represents content from a specific domain directory

type DomainConfig

type DomainConfig struct {
	// Delivery is the default delivery of the domain's skills.
	Delivery string `yaml:"delivery,omitempty" json:"delivery,omitempty" toml:"delivery,omitempty"`
}

DomainConfig is one [domains.<name>] table.

type DomainConfigs

type DomainConfigs map[string]DomainConfig

DomainConfigs maps a domain name to its [domains.<name>] table.

type DomainPluginsConfig

type DomainPluginsConfig struct {
	// Enabled defaults to true when the block is present.
	Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty" toml:"enabled,omitempty"`
	// NamePrefix is prepended to the domain name to form the plugin name.
	NamePrefix string `yaml:"name_prefix,omitempty" json:"name_prefix,omitempty" toml:"name_prefix,omitempty"` //nolint:tagliatelle
	// Include and Exclude select domains by name or glob. Empty Include means all.
	Include []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`
	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty" toml:"exclude,omitempty"`
	// PluginDefaults supplies version, category, keywords, default_enabled and
	// runtimes to every generated plugin.
	PluginDefaults `yaml:",inline"`
}

DomainPluginsConfig is the [marketplace.from_domains] block: one plugin per domain, named NamePrefix + domain.

func (*DomainPluginsConfig) IsEnabled

func (d *DomainPluginsConfig) IsEnabled() bool

IsEnabled reports whether the block is present and not switched off.

type FlatRole

type FlatRole struct {
	RoleConfig
	// Chain is the role followed by its parent, when it has one.
	Chain []string
}

FlatRole is a role with its parent's settings merged in.

type GeminiExtras

type GeminiExtras struct {
	ContextFileName string          `yaml:"context_file_name,omitempty" json:"context_file_name,omitempty" toml:"context_file_name,omitempty"` //nolint:tagliatelle
	Settings        []GeminiSetting `yaml:"settings,omitempty" json:"settings,omitempty" toml:"settings,omitempty"`
	// Commands also bundles the plugin's commands as commands/<name>.toml
	// custom commands (default off).
	Commands bool `yaml:"commands,omitempty" json:"commands,omitempty" toml:"commands,omitempty"`
}

GeminiExtras holds Gemini-extension-specific manifest fields.

type GeminiSetting added in v5.4.0

type GeminiSetting struct {
	Name        string `yaml:"name" json:"name" toml:"name"`
	Description string `yaml:"description" json:"description" toml:"description"`
	EnvVar      string `yaml:"env_var" json:"env_var" toml:"env_var"` //nolint:tagliatelle
	Sensitive   bool   `yaml:"sensitive,omitempty" json:"sensitive,omitempty" toml:"sensitive,omitempty"`
}

GeminiSetting declares a runtime-provided value; credential values are never authored here. https://geminicli.com/docs/extensions/reference/#extension-settings

type GovernanceConfig

type GovernanceConfig struct {
	// RequireApproval selects the content that needs approval: "remote" (includes,
	// installed skills, skill sources and the remote skills the server serves),
	// "local" (every authored item), "all", or "kind:<kind>".
	RequireApproval []string `yaml:"require_approval,omitempty" json:"require_approval,omitempty" toml:"require_approval,omitempty"` //nolint:tagliatelle
	// Exempt lists globs over "kind:domain/id" (or "kind:id") that never need approval.
	Exempt []string `yaml:"exempt,omitempty" json:"exempt,omitempty" toml:"exempt,omitempty"`
	// MinApprovers is how many distinct reviewers must approve one digest. Default 1.
	MinApprovers int `yaml:"min_approvers,omitempty" json:"min_approvers,omitempty" toml:"min_approvers,omitempty"` //nolint:tagliatelle
	// Approvers, when set, is the allowlist of reviewer strings (exact match).
	Approvers []string `yaml:"approvers,omitempty" json:"approvers,omitempty" toml:"approvers,omitempty"`
	// MinAssurance is the weakest assurance level an approval may have to count:
	// "asserted" (default), "review-linked" or "signed".
	MinAssurance string `yaml:"min_assurance,omitempty" json:"min_assurance,omitempty" toml:"min_assurance,omitempty"` //nolint:tagliatelle
	// ApproversFrom names a CODEOWNERS file ("CODEOWNERS" looks in .github/, the
	// repository root and docs/, or a path inside the project): only the owners of
	// an item's source path may approve it.
	ApproversFrom string `yaml:"approvers_from,omitempty" json:"approvers_from,omitempty" toml:"approvers_from,omitempty"` //nolint:tagliatelle
	// ForbidSelfApproval rejects an approval by an author of the content it approves.
	ForbidSelfApproval bool `yaml:"forbid_self_approval,omitempty" json:"forbid_self_approval,omitempty" toml:"forbid_self_approval,omitempty"` //nolint:tagliatelle
	// Teams maps a team ("@org/team") to the reviewers it stands for, so a team
	// in approvers or CODEOWNERS is resolved offline.
	Teams map[string][]string `yaml:"teams,omitempty" json:"teams,omitempty" toml:"teams,omitempty"`
	// MaxAge is the default lifetime of a new approval ("365d", "720h"); empty means no expiry.
	MaxAge string `yaml:"max_age,omitempty" json:"max_age,omitempty" toml:"max_age,omitempty"` //nolint:tagliatelle
	// Enforce makes `lock --check`, `generate --locked` and the skills server fail on
	// content whose approval is missing, stale, expired or insufficient.
	Enforce bool `yaml:"enforce,omitempty" json:"enforce,omitempty" toml:"enforce,omitempty"`

	// PolicyFloor holds the require_approval selectors an organization policy
	// imposes (docs/policy.md). Runtime only: the policy sets it, never the file.
	// Unlike RequireApproval it is not narrowed by Exempt.
	PolicyFloor []string `yaml:"-" json:"-" toml:"-"`
}

GovernanceConfig is the [governance] table: which pinned content needs a recorded reviewer approval in ai-rulez.lock (docs/approvals.md). Without require_approval nothing needs approval.

type GuardConfig

type GuardConfig struct {
	// Generated, when true, makes generation add a PreToolUse hook to every
	// harness with a documented blocking PreToolUse hook. The hook runs
	// `ai-rulez guard`, which blocks edits to files in the generated manifest.
	Generated bool `yaml:"generated,omitempty" json:"generated,omitempty" toml:"generated,omitempty"`
	// Version pins the ai-rulez version the hook runs through npx. Empty means the
	// version of the running binary, or "latest" for a dev build.
	Version string `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	// Command replaces the whole launch command (executable followed by its
	// arguments, without the trailing "guard"), for installs that do not run
	// through npx, for example ["ai-rulez"]. Version is ignored when it is set.
	Command []string `yaml:"command,omitempty" json:"command,omitempty" toml:"command,omitempty"`
}

GuardConfig configures the built-in generated-file guard: a PreToolUse hook that stops an agent from editing a file ai-rulez generated. It is a table of its own because `[hooks]` cannot coexist with the `[[hooks]]` array of tables in TOML.

type HeaderConfig

type HeaderConfig struct {
	Style string `yaml:"style,omitempty" json:"style,omitempty" toml:"style,omitempty"` // "detailed", "compact", or "minimal"
	// Text, when non-empty, replaces the prose generated by Style with an
	// author-supplied banner. It is written verbatim (the generator still wraps
	// it in the output's comment syntax and appends the Content-Hash /
	// Source-Hash freshness lines, subject to Hashes), so a project can point at its own toolchain
	// — a pinned runner, mise, a package manager other than npx — without the
	// predefined style's assumptions. Multi-line TOML via `text = """..."""`.
	Text string `yaml:"text,omitempty" json:"text,omitempty" toml:"text,omitempty"`
	// Timestamp controls whether the "Generated:" line is emitted. Nil means
	// disabled: generated output is byte-reproducible by default, so it can be
	// verified by content hash and two sibling files rendered from the same
	// sources cannot disagree. Set it to true to opt the line back in; pin it
	// with SOURCE_DATE_EPOCH if reproducibility still matters.
	Timestamp *bool `yaml:"timestamp,omitempty" json:"timestamp,omitempty" toml:"timestamp,omitempty"`
	// Hashes selects which freshness lines generated headers carry: "full"
	// writes Content-Hash and Source-Hash, "content" (default) keeps only the
	// per-file Content-Hash, "none" writes neither. Source-Hash covers the whole
	// source set, so under "full" one edit rewrites a line in every generated
	// file; "content" and "none" keep committed output free of that churn.
	Hashes string `yaml:"hashes,omitempty" json:"hashes,omitempty" toml:"hashes,omitempty"`
	// MCPLine, when false, leaves the "Use MCP server: ..." line out of every
	// generated header. Nil means on: the line is derived from [mcp] and is
	// omitted anyway while the self server is off.
	MCPLine *bool `yaml:"mcp_line,omitempty" json:"mcp_line,omitempty" toml:"mcp_line,omitempty"`
	// Nested sets the header of a root that sits inside another ai-rulez root:
	// "compact" (the default) writes one line naming the config, "omit" writes
	// no header, "inherit" writes the same header as the outer root. A custom
	// Text is kept unless Nested is set explicitly.
	Nested string `yaml:"nested,omitempty" json:"nested,omitempty" toml:"nested,omitempty"`
}

HeaderConfig represents header style configuration for generated files

func (*HeaderConfig) GetCustomHeader

func (h *HeaderConfig) GetCustomHeader() string

GetCustomHeader returns the author-supplied header text, or "" when unset.

func (*HeaderConfig) GetHeaderHashes

func (h *HeaderConfig) GetHeaderHashes() string

GetHeaderHashes returns the header hash mode, defaulting to "content".

func (*HeaderConfig) GetHeaderStyle

func (h *HeaderConfig) GetHeaderStyle() string

GetHeaderStyle returns the header style, defaulting to "minimal"

func (*HeaderConfig) ShowMCPLine added in v5.0.4

func (h *HeaderConfig) ShowMCPLine() bool

ShowMCPLine reports whether generated headers may carry the MCP server line.

func (*HeaderConfig) ShowTimestamp

func (h *HeaderConfig) ShowTimestamp() bool

ShowTimestamp reports whether generated headers include the "Generated:" line. Defaults to false, so generation is reproducible unless a project asks for the line.

type HermesExtras

type HermesExtras struct {
	// Source is a project-relative Python module implementing register(ctx).
	// Empty defaults to .ai-rulez/hermes/index.py.
	Source string `yaml:"source,omitempty" json:"source,omitempty" toml:"source,omitempty"`
	// RequiresPython is the Python requirement for the generated wheel.
	// Empty defaults to >=3.11.
	RequiresPython string `yaml:"requires_python,omitempty" json:"requires_python,omitempty" toml:"requires_python,omitempty"` //nolint:tagliatelle
	// Dependencies are PEP 508 requirements installed with the Hermes plugin.
	// Empty leaves both the wheel and project plugin dependency-free.
	Dependencies []string `yaml:"dependencies,omitempty" json:"dependencies,omitempty" toml:"dependencies,omitempty"`
}

HermesExtras holds Hermes-Agent-specific packaging fields.

type HookAction

type HookAction struct {
	Type    string `yaml:"type,omitempty" json:"type,omitempty" toml:"type,omitempty"` // defaults to HookTypeCommand
	Command string `yaml:"command,omitempty" json:"command,omitempty" toml:"command,omitempty"`
	// Script is a project-relative path to a script bundled at hooks/<basename>.
	Script string `yaml:"script,omitempty" json:"script,omitempty" toml:"script,omitempty"`
	// Args are passed to the command as argv. When set, the runtime resolves the
	// command as an executable and spawns it directly, with no shell.
	Args []string `yaml:"args,omitempty" json:"args,omitempty" toml:"args,omitempty"`
	// Timeout is the handler's timeout in seconds; zero leaves the runtime default.
	Timeout int  `yaml:"timeout,omitempty" json:"timeout,omitempty" toml:"timeout,omitempty"`
	Async   bool `yaml:"async,omitempty" json:"async,omitempty" toml:"async,omitempty"`
	// If restricts the handler to matching tool calls, in permission-rule syntax
	// ("Bash(git *)", "Edit(*.ts)") — exactly one rule, with no boolean operators
	// and no expression language. Claude Code evaluates it only on the events in
	// HookEventsEvaluatingIf; on any other event a handler carrying If never runs,
	// so it cannot guard a bootstrap hook. A bootstrap script has to decide for
	// itself whether its work is already done.
	If string `yaml:"if,omitempty" json:"if,omitempty" toml:"if,omitempty"`
	// StatusMessage is shown to the user while the handler runs, which matters for
	// a bootstrap script that blocks the first turn.
	StatusMessage string `yaml:"status_message,omitempty" json:"status_message,omitempty" toml:"status_message,omitempty"` //nolint:tagliatelle
}

HookAction is one action within a HookGroup. Exactly one of Command or Script is required: Command is passed through verbatim and must already resolve in the consumer's environment, while Script is a project-relative file that ai-rulez bundles into the plugin's hooks/ directory and rewrites into a ${PLUGIN_ROOT}-rooted command. Script is what makes a hook self-contained — a bootstrap hook cannot rely on a path that only exists after generation has already run (generated outputs are gitignored, so a fresh clone or a new git worktree has none of them).

type HookGroup

type HookGroup struct {
	Event   string       `yaml:"event" json:"event" toml:"event"` // e.g. SessionStart, PreToolUse
	Matcher string       `yaml:"matcher,omitempty" json:"matcher,omitempty" toml:"matcher,omitempty"`
	Hooks   []HookAction `yaml:"hooks,omitempty" json:"hooks,omitempty" toml:"hooks,omitempty"`
	// Targets restricts a top-level [[hooks]] group to the listed harnesses
	// (claude, codex, cursor, gemini, copilot). Empty means every harness the
	// group can be expressed for.
	Targets []string `yaml:"targets,omitempty" json:"targets,omitempty" toml:"targets,omitempty"`
	// Matchers overrides Matcher per harness, for harnesses that name tools
	// differently from Claude Code (gemini matches `write_file|replace` where
	// Claude matches `Write|Edit`).
	Matchers map[string]string `yaml:"matchers,omitempty" json:"matchers,omitempty" toml:"matchers,omitempty"`
	// Builtin names the feature that synthesized this group (HookBuiltinGuard). It
	// is never read from or written to config.toml.
	Builtin string `yaml:"-" json:"-" toml:"-"`
}

HookGroup is one lifecycle-event hook group. Matcher filters which occurrences of Event run the group (for SessionStart: startup, resume, clear, compact, fork); it is ignored for the events in HookEventsWithoutMatcher.

Targets and Matchers apply to the top-level [[hooks]] only, which render for several harnesses whose event and tool vocabularies differ; a plugin hook group is rendered per runtime and rejects them.

func (*HookGroup) HookTargetsHarness

func (g *HookGroup) HookTargetsHarness(harness string) bool

HookTargetsHarness reports whether a hook group renders for the harness: a group without targets renders for every supported harness.

type ImportedScanFile added in v5.5.0

type ImportedScanFile struct {
	Path    string
	Content string
	Trust   string
}

ImportedScanFile is a file or generated text of an installed plugin that the import-time security scan reads besides the content files: a hook command, a hook script, an MCP server definition. Runtime-only.

type ImportedVerifierFile

type ImportedVerifierFile struct {
	// Include is the name of the include (or installed skill) the file came from.
	Include string
	// Skill is true when it came from an installed skill's verifiers/ directory.
	// A skill's verifiers never use the command predicate, trusted or not.
	Skill bool
	// Name is the file name under the include's verifiers/ directory.
	Name string
	// Data is the file content.
	Data string
}

ImportedVerifierFile is a verifier declaration file that arrived through an include. Its verifiers are data, not trusted code: they may not use the command predicate unless the include is named in [verifiers_settings] trust_exec_from.

func ScanVerifierFiles

func ScanVerifierFiles(configDir string) ([]ImportedVerifierFile, error)

ScanVerifierFiles reads the `*.toml` files of <configDir>/verifiers in name order. Symlinks, non-regular files and files over 1 MiB are skipped, never followed. A missing directory is not an error.

func ScanVerifierFilesIn

func ScanVerifierFilesIn(v workspace.View, configDir string) ([]ImportedVerifierFile, error)

ScanVerifierFilesIn is ScanVerifierFiles reading through v.

type ImproveConfig

type ImproveConfig struct {
	// HoldoutTag marks the cases held out from the optimizer (default "holdout").
	HoldoutTag string `yaml:"holdout_tag,omitempty" json:"holdout_tag,omitempty" toml:"holdout_tag,omitempty"`
	// HoldoutFraction is the deterministic held-out share when no case carries the tag (default 0.3).
	HoldoutFraction *float64 `yaml:"holdout_fraction,omitempty" json:"holdout_fraction,omitempty" toml:"holdout_fraction,omitempty"`
	// MinHoldoutCases is the fewest scored held-out cases a run needs (default 3; it cannot go lower).
	MinHoldoutCases int `yaml:"min_holdout_cases,omitempty" json:"min_holdout_cases,omitempty" toml:"min_holdout_cases,omitempty"`
	// MinGain is the held-out pass-rate gain a candidate needs (default 0.05).
	MinGain *float64 `yaml:"min_gain,omitempty" json:"min_gain,omitempty" toml:"min_gain,omitempty"`
	// MaxRegressions is how many held-out cases may flip from pass to fail (default 0).
	MaxRegressions *int `yaml:"max_regressions,omitempty" json:"max_regressions,omitempty" toml:"max_regressions,omitempty"`
	// Runs is the eval runs per case and arm (default 3).
	Runs int `yaml:"runs,omitempty" json:"runs,omitempty" toml:"runs,omitempty"`
	// MaxRounds is the optimizer invocations (default 3).
	MaxRounds int `yaml:"max_rounds,omitempty" json:"max_rounds,omitempty" toml:"max_rounds,omitempty"`
	// MaxHoldoutEvals is how often the held-out set may be evaluated (default 3).
	MaxHoldoutEvals int `yaml:"max_holdout_evals,omitempty" json:"max_holdout_evals,omitempty" toml:"max_holdout_evals,omitempty"`
	// MaxSkillGrowth bounds SKILL.md growth as a factor of its original tokens (default 1.25, at most 2).
	MaxSkillGrowth float64 `yaml:"max_skill_growth,omitempty" json:"max_skill_growth,omitempty" toml:"max_skill_growth,omitempty"`
	// RequireCIAboveZero makes the bootstrap interval of the gain a gate condition (default false).
	RequireCIAboveZero bool `yaml:"require_ci_above_zero,omitempty" json:"require_ci_above_zero,omitempty" toml:"require_ci_above_zero,omitempty"`
	// Isolation is none (default: the optimizer runs as you), auto (confine when a backend works) or require (refuse without one).
	Isolation string `yaml:"isolation,omitempty" json:"isolation,omitempty" toml:"isolation,omitempty"`
	// EnvPass names environment variables forwarded to the optimizer. Used from a repository config only with --trust-repo-optimizer.
	EnvPass []string `yaml:"env_pass,omitempty" json:"env_pass,omitempty" toml:"env_pass,omitempty"`
	// Optimizer is the optimizer command (argv, no shell). Used from a repository config only with --trust-repo-optimizer.
	Optimizer string `yaml:"optimizer,omitempty" json:"optimizer,omitempty" toml:"optimizer,omitempty"`
}

ImproveConfig is the [improve] table: defaults of `ai-rulez improve run` (docs/improve.md). A flag always wins. optimizer and env_pass choose a command and the environment it receives, so a repository config is honored for them only with --trust-repo-optimizer; the thresholds apply from any config.

func (*ImproveConfig) HasTrustSensitive

func (c *ImproveConfig) HasTrustSensitive() bool

HasTrustSensitive reports whether the table sets a key that must not take effect from a repository config without --trust-repo-optimizer: the optimizer command and the environment it receives.

func (*ImproveConfig) LooserGateKeys

func (c *ImproveConfig) LooserGateKeys() []string

LooserGateKeys names the gate keys of c that are looser than the defaults. A repository config may tighten the acceptance gate but must not weaken it: a hostile or careless table could otherwise accept a candidate that gained nothing (min_gain 0), tolerate regressions, shrink the held-out share or allow a much larger skill. require_ci_above_zero and min_holdout_cases only ever tighten.

func (*ImproveConfig) Validate

func (c *ImproveConfig) Validate() []string

Validate returns the problems of the table, one message each.

type ImproveResolution

type ImproveResolution struct {
	// Effective is the merged table (never nil).
	Effective ImproveConfig
	// IgnoredRepoKeys names the repository keys that were not used because the run did not trust the repository.
	IgnoredRepoKeys []string
	// LoosenedRepoKeys names the repository gate keys that were not used because they are looser than the
	// default (a lower min_gain, more max_regressions, a smaller holdout_fraction, a larger max_skill_growth)
	// and the run did not trust the repository.
	LoosenedRepoKeys []string
	// UserFile is the user config file that was read ("" when none exists).
	UserFile string
}

ImproveResolution is the effective [improve] table of one `improve run`.

type IncludeConfig

type IncludeConfig struct {
	Name          string   `yaml:"name" json:"name" toml:"name"`
	Source        string   `yaml:"source" json:"source" toml:"source"`
	Path          string   `yaml:"path,omitempty" json:"path,omitempty" toml:"path,omitempty"`
	Include       []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`
	Ref           string   `yaml:"ref,omitempty" json:"ref,omitempty" toml:"ref,omitempty"`
	InstallTo     string   `yaml:"install_to,omitempty" json:"install_to,omitempty" toml:"install_to,omitempty"`             //nolint:tagliatelle
	MergeStrategy string   `yaml:"merge_strategy,omitempty" json:"merge_strategy,omitempty" toml:"merge_strategy,omitempty"` //nolint:tagliatelle
	LocalOverride string   `yaml:"local_override,omitempty" json:"local_override,omitempty" toml:"local_override,omitempty"` //nolint:tagliatelle
	// Format names a non-default source layout. "okf" reads the source as an
	// Open Knowledge Format bundle instead of an .ai-rulez directory.
	Format string `yaml:"format,omitempty" json:"format,omitempty" toml:"format,omitempty"`
	// Version is a semver constraint resolved against the repository's tags
	// (see VersionSpec); it excludes Ref.
	Version string `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	// TagPrefix scopes Version to tags that start with it.
	TagPrefix string `yaml:"tag_prefix,omitempty" json:"tag_prefix,omitempty" toml:"tag_prefix,omitempty"` //nolint:tagliatelle
	// IncludePrerelease admits prerelease tags the constraint does not name.
	IncludePrerelease bool `yaml:"include_prerelease,omitempty" json:"include_prerelease,omitempty" toml:"include_prerelease,omitempty"` //nolint:tagliatelle
	// MinReleaseAge holds back a tag younger than this ("7d", "12h", "2w"); needs Version.
	MinReleaseAge string `yaml:"min_release_age,omitempty" json:"min_release_age,omitempty" toml:"min_release_age,omitempty"` //nolint:tagliatelle
}

IncludeConfig represents a content source (git repo or local path)

func (*IncludeConfig) RequestedRef

func (c *IncludeConfig) RequestedRef() string

RequestedRef is what the lock records as the requested ref: the constraint when there is one, else the ref.

func (*IncludeConfig) VersionSpec

func (c *IncludeConfig) VersionSpec() VersionSpec

VersionSpec returns the version constraint of the include.

type InstalledMarketplaceConfig added in v5.5.0

type InstalledMarketplaceConfig struct {
	Name string `yaml:"name" json:"name" toml:"name"`
	// Source is a git URL, a github shorthand (owner/repo), a local path inside
	// the project, or an https URL of a marketplace.json file.
	Source string `yaml:"source" json:"source" toml:"source"`
	// Path is the directory of the repository that holds the marketplace index.
	Path string `yaml:"path,omitempty" json:"path,omitempty" toml:"path,omitempty"`
	Ref  string `yaml:"ref,omitempty" json:"ref,omitempty" toml:"ref,omitempty"`
	// Trust is the scan level of the plugins read through this marketplace.
	Trust string `yaml:"trust,omitempty" json:"trust,omitempty" toml:"trust,omitempty"`
	// Version, TagPrefix, IncludePrerelease and MinReleaseAge are as on IncludeConfig.
	Version           string `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	TagPrefix         string `yaml:"tag_prefix,omitempty" json:"tag_prefix,omitempty" toml:"tag_prefix,omitempty"`                         //nolint:tagliatelle
	IncludePrerelease bool   `yaml:"include_prerelease,omitempty" json:"include_prerelease,omitempty" toml:"include_prerelease,omitempty"` //nolint:tagliatelle
	MinReleaseAge     string `yaml:"min_release_age,omitempty" json:"min_release_age,omitempty" toml:"min_release_age,omitempty"`          //nolint:tagliatelle
}

InstalledMarketplaceConfig is a plugin marketplace the project reads: a git repository, a local directory or an https URL holding a marketplace index (.claude-plugin/marketplace.json, .agents/plugins/marketplace.json or .cursor-plugin/marketplace.json). It installs nothing by itself; an [[installed_plugins]] entry names it as its `marketplace`.

func (*InstalledMarketplaceConfig) RequestedRef added in v5.5.0

func (m *InstalledMarketplaceConfig) RequestedRef() string

RequestedRef is what the lock records as the requested ref.

func (*InstalledMarketplaceConfig) VersionSpec added in v5.5.0

func (m *InstalledMarketplaceConfig) VersionSpec() VersionSpec

VersionSpec returns the version constraint of the marketplace.

type InstalledPluginConfig added in v5.5.0

type InstalledPluginConfig struct {
	Name string `yaml:"name" json:"name" toml:"name"`
	// Marketplace names an [[installed_marketplaces]] entry; the plugin is the
	// marketplace entry called Plugin (default: Name).
	Marketplace string `yaml:"marketplace,omitempty" json:"marketplace,omitempty" toml:"marketplace,omitempty"`
	Plugin      string `yaml:"plugin,omitempty" json:"plugin,omitempty" toml:"plugin,omitempty"`
	// Source, Path and Ref name a plugin repository or directory directly, as
	// on installed_skills. Exclusive with Marketplace.
	Source string `yaml:"source,omitempty" json:"source,omitempty" toml:"source,omitempty"`
	Path   string `yaml:"path,omitempty" json:"path,omitempty" toml:"path,omitempty"`
	Ref    string `yaml:"ref,omitempty" json:"ref,omitempty" toml:"ref,omitempty"`
	// Format forces the plugin layout (claude, codex, gemini, cursor,
	// agent-plugins); empty detects it.
	Format string `yaml:"format,omitempty" json:"format,omitempty" toml:"format,omitempty"`
	// Include keeps only these component kinds (skills, agents, commands, hooks,
	// mcp, rules); Exclude drops them and wins.
	Include []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`
	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty" toml:"exclude,omitempty"`
	// NamePrefix is prepended to every imported skill, agent, command and MCP server
	// name, and to the output directory of a skill.
	NamePrefix string `yaml:"name_prefix,omitempty" json:"name_prefix,omitempty" toml:"name_prefix,omitempty"` //nolint:tagliatelle
	// Domain and Profiles scope the imported content as on installed_skills.
	Domain   string   `yaml:"domain,omitempty" json:"domain,omitempty" toml:"domain,omitempty"`
	Profiles []string `yaml:"profiles,omitempty" json:"profiles,omitempty" toml:"profiles,omitempty"`
	// Trust is the scan level of the imported content: "error" (default) or "warn".
	Trust string `yaml:"trust,omitempty" json:"trust,omitempty" toml:"trust,omitempty"`
	// EnableHooks lets the plugin's hooks run. Off by default: a hook runs a
	// command on every machine that uses the project.
	EnableHooks bool `yaml:"enable_hooks,omitempty" json:"enable_hooks,omitempty" toml:"enable_hooks,omitempty"` //nolint:tagliatelle
	// EnableMCP imports the plugin's MCP servers; on unless set to false.
	EnableMCP *bool `yaml:"enable_mcp,omitempty" json:"enable_mcp,omitempty" toml:"enable_mcp,omitempty"` //nolint:tagliatelle
	// Version, TagPrefix, IncludePrerelease and MinReleaseAge are as on
	// IncludeConfig; they apply to a plugin installed from Source (a plugin of a
	// marketplace follows the marketplace's own pin).
	Version           string `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	TagPrefix         string `yaml:"tag_prefix,omitempty" json:"tag_prefix,omitempty" toml:"tag_prefix,omitempty"`                         //nolint:tagliatelle
	IncludePrerelease bool   `yaml:"include_prerelease,omitempty" json:"include_prerelease,omitempty" toml:"include_prerelease,omitempty"` //nolint:tagliatelle
	MinReleaseAge     string `yaml:"min_release_age,omitempty" json:"min_release_age,omitempty" toml:"min_release_age,omitempty"`          //nolint:tagliatelle
}

InstalledPluginConfig is a plugin whose skills, agents, commands, hooks and MCP servers the project imports. The plugin comes from an installed marketplace (Marketplace) or straight from a repository or directory (Source, Path, Ref).

func (*InstalledPluginConfig) EntryName added in v5.5.0

func (p *InstalledPluginConfig) EntryName() string

EntryName is the marketplace entry the plugin installs.

func (*InstalledPluginConfig) MCPEnabled added in v5.5.0

func (p *InstalledPluginConfig) MCPEnabled() bool

MCPEnabled reports whether the plugin's MCP servers are imported.

func (*InstalledPluginConfig) RequestedRef added in v5.5.0

func (p *InstalledPluginConfig) RequestedRef() string

RequestedRef is what the lock records as the requested ref.

func (*InstalledPluginConfig) VersionSpec added in v5.5.0

func (p *InstalledPluginConfig) VersionSpec() VersionSpec

VersionSpec returns the version constraint of the plugin.

func (*InstalledPluginConfig) Wants added in v5.5.0

func (p *InstalledPluginConfig) Wants(kind string) bool

Wants reports whether the entry imports a component kind.

type InstalledSkillConfig

type InstalledSkillConfig struct {
	Name          string   `yaml:"name" json:"name" toml:"name"`
	Source        string   `yaml:"source" json:"source" toml:"source"`
	Path          string   `yaml:"path,omitempty" json:"path,omitempty" toml:"path,omitempty"`
	Ref           string   `yaml:"ref,omitempty" json:"ref,omitempty" toml:"ref,omitempty"`
	LocalOverride string   `yaml:"local_override,omitempty" json:"local_override,omitempty" toml:"local_override,omitempty"` //nolint:tagliatelle
	Profiles      []string `yaml:"profiles,omitempty" json:"profiles,omitempty" toml:"profiles,omitempty"`
	// Domain places this installed skill in the named domain, so profile and role
	// selection include it exactly like a local domain skill. Empty keeps it in
	// the root content. When both Domain and Profiles are set, both must pass.
	Domain string `yaml:"domain,omitempty" json:"domain,omitempty" toml:"domain,omitempty"`
	// Include keeps only skills whose directory name matches one of these globs,
	// so one entry can select several skills from one repository. Empty selects
	// every skill below the entry's path.
	Include []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`
	// Exclude drops skills whose directory name matches one of these globs; it
	// wins over Include.
	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty" toml:"exclude,omitempty"`
	// Resources narrows or widens what is bundled from each skill's directory. An
	// installed skill is bundled whole by default (everything but SKILL.md, evals/,
	// agents/ and hidden files), so a skill with rules/ or a cli.md beside SKILL.md
	// arrives complete. references/, scripts/ and assets/ are always bundled;
	// setting Resources replaces the default for everything else. A pattern is a
	// name, a path or a glob; a name matches that file or directory at any depth,
	// and a "!" prefix removes what an earlier pattern added. ["none"] (a name no
	// file has) keeps the canonical directories only; ["*"] also bundles evals/,
	// agents/ and hidden files.
	Resources []string `yaml:"resources,omitempty" json:"resources,omitempty" toml:"resources,omitempty"`
	// NamePrefix is prepended to every resolved skill name and to its output
	// directory, so two entries cannot collide.
	NamePrefix string `yaml:"name_prefix,omitempty" json:"name_prefix,omitempty" toml:"name_prefix,omitempty"` //nolint:tagliatelle
	// Trust is the repository-scoped scan level of the imported skills: "error"
	// (every finding counts, the default) or "warn" (only error-severity
	// findings block). See TrustError and TrustWarn.
	Trust string `yaml:"trust,omitempty" json:"trust,omitempty" toml:"trust,omitempty"`
	// Version, TagPrefix and IncludePrerelease are as on IncludeConfig.
	Version           string `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	TagPrefix         string `yaml:"tag_prefix,omitempty" json:"tag_prefix,omitempty" toml:"tag_prefix,omitempty"`                         //nolint:tagliatelle
	IncludePrerelease bool   `yaml:"include_prerelease,omitempty" json:"include_prerelease,omitempty" toml:"include_prerelease,omitempty"` //nolint:tagliatelle
	MinReleaseAge     string `yaml:"min_release_age,omitempty" json:"min_release_age,omitempty" toml:"min_release_age,omitempty"`          //nolint:tagliatelle
}

InstalledSkillConfig represents a named skill to install from an external source

func (*InstalledSkillConfig) GetPath

func (s *InstalledSkillConfig) GetPath() string

GetPath returns the path within the repo that holds the entry's skills. An entry that selects several skills with include/exclude and names no path is a directory of skill directories: it defaults to the conventional "skills" directory, so `name` is only the entry's label. A single-skill entry keeps its "skills/<name>" default.

func (*InstalledSkillConfig) RequestedRef

func (s *InstalledSkillConfig) RequestedRef() string

RequestedRef is what the lock records as the requested ref.

func (*InstalledSkillConfig) SelectsSeveral added in v5.0.2

func (s *InstalledSkillConfig) SelectsSeveral() bool

SelectsSeveral reports whether the entry picks skills by include/exclude rather than naming one skill directory with `name`.

func (*InstalledSkillConfig) VersionSpec

func (s *InstalledSkillConfig) VersionSpec() VersionSpec

VersionSpec returns the version constraint of the installed skill.

type JudgeConfig added in v5.4.0

type JudgeConfig struct {
	// Categories limits the review to security, quality and governance (default all).
	Categories []string `yaml:"categories,omitempty" json:"categories,omitempty" toml:"categories,omitempty"`
	// FailOn lists "<category>:<severity>", "<category>:none", "score:<n>" or "<category>:score:<n>"; the
	// default is security:high, governance:high, quality:none.
	FailOn []string `yaml:"fail_on,omitempty" json:"fail_on,omitempty" toml:"fail_on,omitempty"` //nolint:tagliatelle
	// MinConfidence drops findings below it: low (default), medium or high.
	MinConfidence string `yaml:"min_confidence,omitempty" json:"min_confidence,omitempty" toml:"min_confidence,omitempty"` //nolint:tagliatelle
	// AttachRules attaches the path-scoped rules that govern a unit to its governance review (default true).
	AttachRules *bool `yaml:"attach_rules,omitempty" json:"attach_rules,omitempty" toml:"attach_rules,omitempty"` //nolint:tagliatelle
	// AttachClaudeMD attaches the nested CLAUDE.md and AGENTS.md files that apply to a unit (default true).
	AttachClaudeMD *bool `yaml:"attach_claude_md,omitempty" json:"attach_claude_md,omitempty" toml:"attach_claude_md,omitempty"` //nolint:tagliatelle
	// Skills names the rubric skills a run applies.
	Skills *JudgeSkills `yaml:"skills,omitempty" json:"skills,omitempty" toml:"skills,omitempty"`
	// Prompts extends or replaces a category's role prompt, by category name.
	Prompts map[string]JudgePrompt `yaml:"prompts,omitempty" json:"prompts,omitempty" toml:"prompts,omitempty"`
	// Suppress silences known findings, pinned to their quote and expiring.
	Suppress []JudgeSuppression `yaml:"suppress,omitempty" json:"suppress,omitempty" toml:"suppress,omitempty"`
	// TrapsFile is a YAML list of repository false-positive shapes for the adjudicator (default .ai-rulez/judge/traps.yaml).
	TrapsFile string `yaml:"traps_file,omitempty" json:"traps_file,omitempty" toml:"traps_file,omitempty"` //nolint:tagliatelle

	// Models names the model of each role (security, quality, governance, adjudicator, or reviewers for the first three).
	Models map[string]string `yaml:"models,omitempty" json:"models,omitempty" toml:"models,omitempty"`
	// MaxCostUSD, MaxTokens and MaxCalls are the spend caps of a run (0 = the command default / none).
	MaxCostUSD float64 `yaml:"max_cost_usd,omitempty" json:"max_cost_usd,omitempty" toml:"max_cost_usd,omitempty"` //nolint:tagliatelle
	MaxTokens  int     `yaml:"max_tokens,omitempty" json:"max_tokens,omitempty" toml:"max_tokens,omitempty"`       //nolint:tagliatelle
	MaxCalls   int     `yaml:"max_calls,omitempty" json:"max_calls,omitempty" toml:"max_calls,omitempty"`          //nolint:tagliatelle
	// Concurrency and RPM bound the calls in flight and started per minute.
	Concurrency int `yaml:"concurrency,omitempty" json:"concurrency,omitempty" toml:"concurrency,omitempty"`
	RPM         int `yaml:"rpm,omitempty" json:"rpm,omitempty" toml:"rpm,omitempty"`
	// AllowLLM agrees to send content to the model's provider without --allow-llm.
	AllowLLM bool `yaml:"allow_llm,omitempty" json:"allow_llm,omitempty" toml:"allow_llm,omitempty"` //nolint:tagliatelle
	// AllowPromptOverride lets an `override` prompt mode take effect from a repository file.
	AllowPromptOverride bool `yaml:"allow_prompt_override,omitempty" json:"allow_prompt_override,omitempty" toml:"allow_prompt_override,omitempty"` //nolint:tagliatelle
}

JudgeConfig is the [judge] table: what `ai-rulez judge` reviews and how it gates. The keys of the first group are honored from the repository config; the second group names where a run sends content and what it may spend, so only the user config file, the environment and flags can set it. A repository value there is ignored and reported (see ResolveJudge).

func (*JudgeConfig) AttachClaudeMDOn added in v5.4.0

func (j *JudgeConfig) AttachClaudeMDOn() bool

AttachClaudeMDOn reports AttachClaudeMD with its default.

func (*JudgeConfig) AttachRulesOn added in v5.4.0

func (j *JudgeConfig) AttachRulesOn() bool

attach reports AttachRules with its default.

func (*JudgeConfig) Validate added in v5.4.0

func (j *JudgeConfig) Validate(now time.Time) []string

Validate returns the problems of the table, one message each.

type JudgePrompt added in v5.4.0

type JudgePrompt struct {
	// Mode is extend (the file's text is added to the reviewer's instructions) or override (it replaces the role prompt).
	Mode string `yaml:"mode,omitempty" json:"mode,omitempty" toml:"mode,omitempty"`
	// File is the prompt text, relative to the project root.
	File string `yaml:"file,omitempty" json:"file,omitempty" toml:"file,omitempty"`
}

JudgePrompt is one [judge.prompts.<category>] entry.

type JudgeResolution added in v5.4.0

type JudgeResolution struct {
	// Repo is the repository table (nil when absent) without the user-scope-only keys.
	Repo *JudgeConfig
	// User is the user config file's table (nil when absent).
	User *JudgeConfig
	// IgnoredRepoKeys names the user-scope-only keys the repository set; they have no effect.
	IgnoredRepoKeys []string
	// UserFile is the user config path that was read ("" when none exists).
	UserFile string
}

JudgeResolution is the effective [judge] setup: the repository table with its user-scope-only keys removed, and the user table those keys come from.

type JudgeSkills added in v5.4.0

type JudgeSkills struct {
	// Load lists skills (by name, from the project's skill directories) applied to the governance review.
	Load []string `yaml:"load,omitempty" json:"load,omitempty" toml:"load,omitempty"`
	// For assigns skills to a category: for = { quality = ["my-rubric"] }.
	For map[string][]string `yaml:"for,omitempty" json:"for,omitempty" toml:"for,omitempty"`
	// Pins maps a skill name to the start of its SHA-256: a pinned skill that matches is trusted as
	// configured text (tier T1); an unpinned or changed one is reference data (T2).
	Pins map[string]string `yaml:"pins,omitempty" json:"pins,omitempty" toml:"pins,omitempty"`
}

JudgeSkills is the [judge.skills] table.

type JudgeSuppression added in v5.4.0

type JudgeSuppression struct {
	Code    string `yaml:"code" json:"code" toml:"code"`
	Path    string `yaml:"path" json:"path" toml:"path"`
	Quote   string `yaml:"quote" json:"quote" toml:"quote"`
	Reason  string `yaml:"reason" json:"reason" toml:"reason"`
	Expires string `yaml:"expires" json:"expires" toml:"expires"`
}

JudgeSuppression is one [[judge.suppress]] entry.

type KimiExtras

type KimiExtras struct {
	SkillInstructions string `yaml:"skill_instructions,omitempty" json:"skill_instructions,omitempty" toml:"skill_instructions,omitempty"`    //nolint:tagliatelle
	SessionStartSkill string `yaml:"session_start_skill,omitempty" json:"session_start_skill,omitempty" toml:"session_start_skill,omitempty"` //nolint:tagliatelle
}

KimiExtras holds Kimi-plugin-specific manifest fields.

type LLMResolution

type LLMResolution struct {
	Config llm.Config
	// Ignored names the user-scope-only keys the repository config (or its local
	// overlay) set; they have no effect. Set them in the user config file or via
	// AI_RULEZ_LLM_* instead.
	Ignored []string
	// UserFile is the user config path that was read ("" when none exists).
	UserFile string
}

LLMResolution is the effective llm setup and what the trust rule dropped.

type LLMsTxtConfig

type LLMsTxtConfig struct {
	// Dir is the directory llms.txt is written to, relative to the project root.
	// Empty means the project root.
	Dir string `yaml:"dir,omitempty" json:"dir,omitempty" toml:"dir,omitempty"`
	// Title is the H1 of the file. Empty means the project name.
	Title string `yaml:"title,omitempty" json:"title,omitempty" toml:"title,omitempty"`
	// Summary is the blockquote under the title. Empty means the project description.
	Summary string `yaml:"summary,omitempty" json:"summary,omitempty" toml:"summary,omitempty"`
	// Full also writes llms-full.txt, which holds the full text of every listed item.
	Full bool `yaml:"full,omitempty" json:"full,omitempty" toml:"full,omitempty"`
	// Include limits the content kinds listed (rules, context, skills, agents,
	// commands). Empty means rules, context and skills. Agents and commands are
	// listed in the Optional section.
	Include []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`
}

LLMsTxtConfig configures the llms-txt preset.

type Layout added in v5.2.0

type Layout struct {
	// ConfigDir is the directory that holds the main config file.
	ConfigDir string
	// ConfigFile is the main config file.
	ConfigFile string
	// ContentDir holds the rules, context, skills, agents, commands, checks and
	// domains directories: .agents for the "agents" source, ConfigDir otherwise.
	ContentDir string
	// StateDir holds the manifest, lock, local/ and config.local.toml.
	StateDir string
	// Agents is true when the content is read from AGENTS.md and .agents.
	Agents bool
	// AgentsMDSplit is true when AGENTS.md is read as one context item per H2
	// heading (agents_md_split).
	AgentsMDSplit bool
	// Contentless is true for the root layout (ai-rulez.toml, no content tree):
	// ContentDir names a directory that does not exist and content cannot be written.
	Contentless bool
}

Layout says where the pieces of a project live, given the config directory a command was pointed at (.ai-rulez, .config/ai-rulez or .agents). The CRUD commands and the MCP server use it to write content, the config file and the machine-local tree to the right place without loading the project.

func LayoutOf added in v5.2.0

func LayoutOf(configDir string) Layout

LayoutOf resolves the layout of the config directory configDir. It reads the config file only to learn its `source`; an unreadable file means the default.

type LintAccept added in v5.6.2

type LintAccept struct {
	// Rule is one finding code (AR007) or name (tool-breadth); no wildcards.
	Rule string `yaml:"rule" json:"rule" toml:"rule"`
	// Path is a glob, relative to the config directory or the repository
	// root, of the source file; an exact path is a glob without wildcards.
	Path string `yaml:"path" json:"path" toml:"path"`
	// Match is a substring the finding's message must contain. Set Match,
	// Line or both; an entry with neither would accept the whole file.
	Match string `yaml:"match,omitempty" json:"match,omitempty" toml:"match,omitempty"`
	// Line is the 1-based line the finding must be reported on.
	Line int `yaml:"line,omitempty" json:"line,omitempty" toml:"line,omitempty"`
	// Reason says why the finding is acceptable; it is required.
	Reason string `yaml:"reason" json:"reason" toml:"reason"`
}

LintAccept accepts the findings of one rule in one file whose message holds Match (or that sit on Line), with a required reason: a hand-written, reviewable alternative to a generated baseline entry.

type LintBudget

type LintBudget struct {
	MaxLines  int `yaml:"max_lines,omitempty" json:"max_lines,omitempty" toml:"max_lines,omitempty"`    //nolint:tagliatelle
	MaxTokens int `yaml:"max_tokens,omitempty" json:"max_tokens,omitempty" toml:"max_tokens,omitempty"` //nolint:tagliatelle
}

LintBudget caps the size of one content item. Zero keeps the default for the kind; a negative value removes the limit.

type LintBuiltinNames added in v5.5.3

type LintBuiltinNames struct {
	// Enabled turns the checks off when false; unset or true runs them.
	Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty" toml:"enabled,omitempty"`
	// Allow lists collisions that are intended: "name" (any harness) or "preset:name".
	// It does not silence AR9U1, which the harness enforces. lint.allow_overrides is
	// honored the same way.
	Allow []string `yaml:"allow,omitempty" json:"allow,omitempty" toml:"allow,omitempty"`
	// Extra maps a preset to built-in names the embedded table cannot know: commands of
	// the organization, MCP prompts, plugin skills, user-level skills.
	Extra map[string][]string `yaml:"extra,omitempty" json:"extra,omitempty" toml:"extra,omitempty"`
}

LintBuiltinNames configures the checks for a skill, command or agent that is named like a built-in of a harness that loads it (AR9U0) or like a name the harness refuses to load (AR9U1).

func (*LintBuiltinNames) BuiltinNamesEnabled added in v5.5.3

func (b *LintBuiltinNames) BuiltinNamesEnabled() bool

BuiltinNamesEnabled reports whether the checks run. Safe on a nil receiver.

type LintCapability

type LintCapability struct {
	// MaxNetworkCommands is how many network commands an item may run before
	// AR030 calls it network-heavy. Unset keeps the default (5); 0 is valid.
	MaxNetworkCommands *int `yaml:"max_network_commands,omitempty" json:"max_network_commands,omitempty" toml:"max_network_commands,omitempty"` //nolint:tagliatelle
}

LintCapability tunes the capability analysis (AR030).

type LintConfig

type LintConfig struct {
	// Profile selects a preset of severities and the failure threshold:
	// "default", "strict" or "permissive". Explicit severity, fail_on and
	// budget settings win over the preset.
	Profile string `yaml:"profile,omitempty" json:"profile,omitempty" toml:"profile,omitempty"`
	// FailOn is the lowest severity that makes the command exit non-zero:
	// "error" (default), "warning", or "none".
	FailOn string `yaml:"fail_on,omitempty" json:"fail_on,omitempty" toml:"fail_on,omitempty"` //nolint:tagliatelle
	// Analyzers is an allow-list of lint analyzers (security, references,
	// hooks, ...): only these run. `validate --analyzer` replaces it for one
	// invocation. Empty runs every analyzer.
	Analyzers []string `yaml:"analyzers,omitempty" json:"analyzers,omitempty" toml:"analyzers,omitempty"`
	// Ignore lists finding codes (AR401) or names (path-missing) to drop.
	Ignore []string `yaml:"ignore,omitempty" json:"ignore,omitempty" toml:"ignore,omitempty"`
	// IgnorePaths lists globs, relative to the config directory, of source
	// files whose findings are dropped.
	IgnorePaths []string `yaml:"ignore_paths,omitempty" json:"ignore_paths,omitempty" toml:"ignore_paths,omitempty"` //nolint:tagliatelle
	// TrackedOnly reports no findings in git-untracked files, so the result does
	// not depend on what a local run left behind (a __pycache__, a build
	// artifact). The files are still walked and linted; their findings are
	// dropped after the run. `validate --tracked-only` sets it for one
	// invocation; files named by --changed are kept, and outside a git work tree
	// it is ignored with a warning.
	TrackedOnly bool `yaml:"tracked_only,omitempty" json:"tracked_only,omitempty" toml:"tracked_only,omitempty"` //nolint:tagliatelle
	// IgnoreRules drops findings of the listed rules in the listed files only:
	// `[[lint.ignore_rules]] rules = ["AR901", "AR9*"] paths = ["playground/**"]`.
	IgnoreRules []LintIgnoreRule `yaml:"ignore_rules,omitempty" json:"ignore_rules,omitempty" toml:"ignore_rules,omitempty"` //nolint:tagliatelle
	// Accept accepts single findings with a reason:
	// `[[lint.accept]] rule = "AR007" path = ".claude/skills/foo/SKILL.md" match = "Bash" reason = "..."`.
	// Accepted findings stay in the report, marked accepted, and do not fail
	// the run; an entry that matches nothing is reported (AR9I0).
	Accept []LintAccept `yaml:"accept,omitempty" json:"accept,omitempty" toml:"accept,omitempty"`
	// SkillRoots lists globs, relative to the project root, of the directories
	// that hold skill directories (".claude/skills", "plugins/*/skills") a
	// harness loads from. AR808 checks a hand-written skill or command only in
	// these directories; unset, the harness's own roots, a plugin's skills/ and
	// local skill sources are used. Authored content is always checked.
	SkillRoots []string `yaml:"skill_roots,omitempty" json:"skill_roots,omitempty" toml:"skill_roots,omitempty"` //nolint:tagliatelle
	// Severity overrides the severity of a code or name: error, warning, info, off.
	Severity map[string]string `yaml:"severity,omitempty" json:"severity,omitempty" toml:"severity,omitempty"`
	// ExamplePaths lists globs, relative to the config directory or the repo
	// root, of files that document risky commands as examples: findings of the
	// command-shaped rules (AR005, AR006, AR008) in them are dropped.
	ExamplePaths []string `yaml:"example_paths,omitempty" json:"example_paths,omitempty" toml:"example_paths,omitempty"` //nolint:tagliatelle
	// AllowPaths lists globs of repo paths that may be referenced without
	// existing in the tracked tree (generated or machine-local outputs).
	AllowPaths []string `yaml:"allow_paths,omitempty" json:"allow_paths,omitempty" toml:"allow_paths,omitempty"` //nolint:tagliatelle
	// KnownNames lists skill, agent, rule, or command names provided outside
	// this tree (for example by an installed plugin).
	KnownNames []string `yaml:"known_names,omitempty" json:"known_names,omitempty" toml:"known_names,omitempty"` //nolint:tagliatelle
	// Description tunes the frontmatter description checks.
	Description *LintDescription `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	// Ratchet tolerates up to N findings per rule code or name: a rule whose
	// unaccepted findings number at most N does not count toward the exit code.
	// Lower N over time to ratchet a rule down. Not to be confused with Budgets,
	// which caps the size of a content item.
	Ratchet map[string]int `yaml:"ratchet,omitempty" json:"ratchet,omitempty" toml:"ratchet,omitempty"`
	// Tolerate and Budget are the pre-v5 spellings of Ratchet. They are only
	// decoded so the loader can refuse them with a pointer to `ai-rulez migrate v5`.
	Tolerate map[string]int `yaml:"tolerate,omitempty" json:"tolerate,omitempty" toml:"tolerate,omitempty"`
	Budget   map[string]int `yaml:"budget,omitempty" json:"budget,omitempty" toml:"budget,omitempty"`
	// Risk sets the weights of the advisory risk score.
	Risk *LintRisk `yaml:"risk,omitempty" json:"risk,omitempty" toml:"risk,omitempty"`
	// Budgets maps a content kind (rule, context, skill, agent, command) to its size
	// limits (max_lines, max_tokens). Not to be confused with Ratchet.
	Budgets map[string]LintBudget `yaml:"budgets,omitempty" json:"budgets,omitempty" toml:"budgets,omitempty"`
	// Capability tunes the capability analysis (AR030).
	Capability *LintCapability `yaml:"capability,omitempty" json:"capability,omitempty" toml:"capability,omitempty"`
	// LoadBudgets overrides the harness load limits AR964 checks, keyed by the
	// limit's id (for example "claude-skill-listing"); the value is in the
	// limit's own unit. Not to be confused with Budgets, which caps the size of
	// a content kind.
	LoadBudgets map[string]int `yaml:"load_budgets,omitempty" json:"load_budgets,omitempty" toml:"load_budgets,omitempty"` //nolint:tagliatelle
	// RequireMetadata maps a content kind to frontmatter keys every item of that kind must set.
	RequireMetadata map[string][]string `yaml:"require_metadata,omitempty" json:"require_metadata,omitempty" toml:"require_metadata,omitempty"` //nolint:tagliatelle
	// AllowOverrides lists content names ("name") or domain-qualified names
	// ("domain/name") whose shadowing of another copy is intentional, so they
	// are not reported as collapsed duplicates. "name" or "preset:name" also
	// acknowledges a skill or command named like a harness built-in (AR808).
	AllowOverrides []string `yaml:"allow_overrides,omitempty" json:"allow_overrides,omitempty" toml:"allow_overrides,omitempty"` //nolint:tagliatelle
	// AllowedKeys lists frontmatter keys accepted in addition to the built-in
	// Agent Skills, Claude Code and ai-rulez keys.
	AllowedKeys []string `yaml:"allowed_keys,omitempty" json:"allowed_keys,omitempty" toml:"allowed_keys,omitempty"` //nolint:tagliatelle
	// Metadata types and bounds the values of frontmatter keys (top-level, or
	// inside the Agent Skills `metadata` map), keyed by the frontmatter key.
	Metadata map[string]LintMetadataRule `yaml:"metadata,omitempty" json:"metadata,omitempty" toml:"metadata,omitempty"`
	// Security configures the security rule family (AR001...).
	Security *LintSecurity `yaml:"security,omitempty" json:"security,omitempty" toml:"security,omitempty"`
	// External lists third-party scanners whose findings are merged into the
	// report when `validate --external` (or `scan --external`) runs.
	External []LintExternal `yaml:"external,omitempty" json:"external,omitempty" toml:"external,omitempty"`
	// ScannerPolicy sets the policy for the external scanners as a whole: an
	// embedded preset, required scanners, a failure threshold, isolation and
	// which egress scanners may be allowed.
	ScannerPolicy *LintScannerPolicy `yaml:"scanner_policy,omitempty" json:"scanner_policy,omitempty" toml:"scanner_policy,omitempty"` //nolint:tagliatelle
	// Evals configures the evals-missing check (AR962).
	Evals *LintEvals `yaml:"evals,omitempty" json:"evals,omitempty" toml:"evals,omitempty"`
	// Traps configures the harness trap checks (AR9C1...).
	Traps *LintTraps `yaml:"traps,omitempty" json:"traps,omitempty" toml:"traps,omitempty"`
	// BuiltinNames configures the harness built-in name checks (AR9U0, AR9U1).
	BuiltinNames *LintBuiltinNames `yaml:"builtin_names,omitempty" json:"builtin_names,omitempty" toml:"builtin_names,omitempty"` //nolint:tagliatelle
	// AgentsMDOnly turns on the CLAUDE.md shadow check (AR9Q0): when AGENTS.md
	// is canonical for Claude Code, every tracked CLAUDE.md, .claude/CLAUDE.md
	// and CLAUDE.local.md that is not the generated "@AGENTS.md" shim is
	// reported. Off by default, so existing projects are unchanged.
	AgentsMDOnly bool `yaml:"agents_md_only,omitempty" json:"agents_md_only,omitempty" toml:"agents_md_only,omitempty"` //nolint:tagliatelle
	// AgentsMDOnlyAllow lists path globs (relative to the config directory or
	// the repo root) of CLAUDE.md files the check accepts as intentional islands.
	AgentsMDOnlyAllow []string `yaml:"agents_md_only_allow,omitempty" json:"agents_md_only_allow,omitempty" toml:"agents_md_only_allow,omitempty"` //nolint:tagliatelle
	// Paths lists files and directories, relative to the project root, that
	// `validate` and `scan` lint in addition to the content tree, for example a
	// marketplace or a skill kept outside .ai-rulez/. Each entry must resolve
	// inside the project root; the command line's --path and --glob add to it.
	Paths []string `yaml:"paths,omitempty" json:"paths,omitempty" toml:"paths,omitempty"`
	// LintOutputs also lints the generated files (the bytes `generate` writes)
	// with the analyzers that judge what a model reads: security, references and
	// descriptions. The same as `validate --outputs`.
	LintOutputs bool `yaml:"lint_outputs,omitempty" json:"lint_outputs,omitempty" toml:"lint_outputs,omitempty"` //nolint:tagliatelle
}

LintConfig configures `ai-rulez validate`. Every field is optional; the lint package applies defaults for anything left unset.

type LintDescription

type LintDescription struct {
	// MinLength and MaxLength bound the description in characters. Zero keeps the default.
	MinLength int `yaml:"min_length,omitempty" json:"min_length,omitempty" toml:"min_length,omitempty"` //nolint:tagliatelle
	MaxLength int `yaml:"max_length,omitempty" json:"max_length,omitempty" toml:"max_length,omitempty"` //nolint:tagliatelle
	// RequireUseWhen asks that a skill or agent description state when to use it ("Use when ...").
	RequireUseWhen bool `yaml:"require_use_when,omitempty" json:"require_use_when,omitempty" toml:"require_use_when,omitempty"` //nolint:tagliatelle
	// NearDuplicateThreshold is the word-set similarity (0-1) at which two descriptions are reported.
	NearDuplicateThreshold float64 `yaml:"near_duplicate_threshold,omitempty" json:"near_duplicate_threshold,omitempty" toml:"near_duplicate_threshold,omitempty"` //nolint:tagliatelle
}

LintDescription tunes description quality checks.

type LintEvals

type LintEvals struct {
	// Require turns the evals-missing check on at warning severity. An explicit
	// [lint.severity] entry for evals-missing wins.
	Require bool `yaml:"require,omitempty" json:"require,omitempty" toml:"require,omitempty"`
	// Allow lists skill names or globs exempt from the check.
	Allow []string `yaml:"allow,omitempty" json:"allow,omitempty" toml:"allow,omitempty"`
	// MinPassRate turns the eval-score-low check (AR998) on: a skill whose recorded
	// pass rate (.ai-rulez/eval-results.json) is below it is reported. Range 0-1;
	// also the pass mark of "ai-rulez eval run" when --threshold is not given.
	MinPassRate float64 `yaml:"min_pass_rate,omitempty" json:"min_pass_rate,omitempty" toml:"min_pass_rate,omitempty"` //nolint:tagliatelle
	// RequireFresh turns the eval-stale check (AR997) on: "warn" or "error" report a
	// skill edited after its last recorded passing eval; "off" or empty disables it.
	RequireFresh string `yaml:"require_fresh,omitempty" json:"require_fresh,omitempty" toml:"require_fresh,omitempty"` //nolint:tagliatelle
	// MinActivationRecall and MinActivationPrecision turn the activation-low check
	// (AR9A1) on: a skill whose recorded activation recall or precision
	// ("ai-rulez eval run --mode activation") is below it is reported. Range 0-1.
	MinActivationRecall    float64 `yaml:"min_activation_recall,omitempty" json:"min_activation_recall,omitempty" toml:"min_activation_recall,omitempty"`          //nolint:tagliatelle
	MinActivationPrecision float64 `yaml:"min_activation_precision,omitempty" json:"min_activation_precision,omitempty" toml:"min_activation_precision,omitempty"` //nolint:tagliatelle
	// ConfusionThreshold turns the skill-confusable check (AR9A2) on: a sibling that
	// won at least this share (0-1) of a skill's positive activation prompts is reported.
	ConfusionThreshold float64 `yaml:"confusion_threshold,omitempty" json:"confusion_threshold,omitempty" toml:"confusion_threshold,omitempty"` //nolint:tagliatelle
	// Estimate overrides the assumptions behind the eval cost estimate;
	// `ai-rulez eval calibrate-estimate` proposes values measured from earlier runs.
	Estimate *LintEvalsEstimate `yaml:"estimate,omitempty" json:"estimate,omitempty" toml:"estimate,omitempty"`
}

LintEvals configures the check for skills that have no eval cases. A skill has cases when its evals/ directory, or .ai-rulez/evals/<skill-name>/, holds at least one file.

type LintEvalsEstimate

type LintEvalsEstimate struct {
	// OverheadTokens is the harness's own input per agent run (system prompt, tools).
	OverheadTokens int `yaml:"overhead_tokens,omitempty" json:"overhead_tokens,omitempty" toml:"overhead_tokens,omitempty"` //nolint:tagliatelle
	// AssumedOutputTokens is the output per agent run of a case run.
	AssumedOutputTokens int `yaml:"assumed_output_tokens,omitempty" json:"assumed_output_tokens,omitempty" toml:"assumed_output_tokens,omitempty"` //nolint:tagliatelle
	// ActivationOutputTokens is the output per activation decision.
	ActivationOutputTokens int `yaml:"activation_output_tokens,omitempty" json:"activation_output_tokens,omitempty" toml:"activation_output_tokens,omitempty"` //nolint:tagliatelle
	// ToolLoopFactor scales the tool-loop re-read in the high figure.
	ToolLoopFactor float64 `yaml:"tool_loop_factor,omitempty" json:"tool_loop_factor,omitempty" toml:"tool_loop_factor,omitempty"` //nolint:tagliatelle
	// PriceInPerMTok and PriceOutPerMTok are the USD per million tokens the runs are
	// really billed at (a harness with prompt caching bills most input at a fraction
	// of the list price). --price-in and --price-out win; zero keeps the price table.
	PriceInPerMTok  float64 `yaml:"price_in_per_mtok,omitempty" json:"price_in_per_mtok,omitempty" toml:"price_in_per_mtok,omitempty"`    //nolint:tagliatelle
	PriceOutPerMTok float64 `yaml:"price_out_per_mtok,omitempty" json:"price_out_per_mtok,omitempty" toml:"price_out_per_mtok,omitempty"` //nolint:tagliatelle
}

LintEvalsEstimate holds the assumptions of the eval cost estimate. Zero keeps the built-in value.

type LintExternal

type LintExternal struct {
	Name    string   `yaml:"name" json:"name" toml:"name"`
	Command []string `yaml:"command" json:"command" toml:"command"`
	// Format is "sarif" (default) or "json".
	Format string `yaml:"format,omitempty" json:"format,omitempty" toml:"format,omitempty"`
	// Egress declares whether content derived from the scanned files can leave
	// the machine (or a credential is used to call a network service). Unset keeps
	// the legacy behavior (full inherited environment) and is reported as AR9E1.
	// false scrubs the environment and rejects known egress flags; true runs the
	// scanner only when its name is passed to --allow-egress.
	Egress *bool `yaml:"egress,omitempty" json:"egress,omitempty" toml:"egress,omitempty"`
	// Timeout is a Go duration such as "90s"; default 2m, at most 15m (or
	// [lint.scanner_policy] max_timeout). It bounds each run of the command: each
	// batch of a batch_size scanner gets the whole timeout.
	Timeout string `yaml:"timeout,omitempty" json:"timeout,omitempty" toml:"timeout,omitempty"`
	// EnvPass lists extra environment variable names passed through the scrubbed
	// environment. Proxy and credential-like names are rejected when egress is false.
	EnvPass []string `yaml:"env_pass,omitempty" json:"env_pass,omitempty" toml:"env_pass,omitempty"` //nolint:tagliatelle
	// Inputs turns staging on: the scanner runs in a scratch directory that holds
	// a read-only copy of exactly these kinds of content (rules, context, skills,
	// agents, commands, checks, hooks, mcp, imports) and nothing else. The
	// placeholders {stage}, {root}, {project}, {files}, {skill_dirs}, {out} and {tmp} in
	// command are expanded as argv elements; no file path is appended.
	Inputs []string `yaml:"inputs,omitempty" json:"inputs,omitempty" toml:"inputs,omitempty"`
	// SeverityMap maps a scanner rule id (glob) to a severity: critical, high,
	// medium, low, info (or error, warning). It is applied before any other mapping.
	SeverityMap map[string]string `yaml:"severity_map,omitempty" json:"severity_map,omitempty" toml:"severity_map,omitempty"` //nolint:tagliatelle
	// MaxSeverity caps the severity of this scanner's findings (same values).
	MaxSeverity string `yaml:"max_severity,omitempty" json:"max_severity,omitempty" toml:"max_severity,omitempty"` //nolint:tagliatelle
	// Profile names an embedded scanner profile (command template, format,
	// inputs, egress declaration, severity map, flag deny-list). Keys set on the
	// entry override the profile's; egress cannot be declared weaker than the
	// profile's.
	Profile string `yaml:"profile,omitempty" json:"profile,omitempty" toml:"profile,omitempty"`
	// Version is a semantic version range the scanner's --version output must
	// satisfy (">=1.0.0, <2"); a scanner outside it is not run (AR9E2).
	Version string `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	// VersionCommand is the argv that prints the scanner's version for the
	// Version check. Default: command[0] --version, which names the launcher
	// (python3, node, uvx) rather than the scanner when command[0] is one. It runs
	// from the project root, in the scanner's isolation mode; needs Version.
	VersionCommand []string `yaml:"version_command,omitempty" json:"version_command,omitempty" toml:"version_command,omitempty"` //nolint:tagliatelle
	// VersionTimeout bounds the version probe (a duration such as "30s"; default
	// 10s). A probe that exceeds it is reported as timed out (AR9E2); needs Version.
	VersionTimeout string `yaml:"version_timeout,omitempty" json:"version_timeout,omitempty" toml:"version_timeout,omitempty"` //nolint:tagliatelle
	// BatchSize splits the skill directories handed to a command that passes
	// {skill_dirs} into runs of at most this many directories, each with its own
	// timeout and cached on its own. 0 (default) hands the scanner all of them
	// at once, subject to the command-line size limit.
	BatchSize int `yaml:"batch_size,omitempty" json:"batch_size,omitempty" toml:"batch_size,omitempty"` //nolint:tagliatelle
	// Parallel runs up to this many batches at the same time (1 to 16; default 1).
	// Each batch gets its own {out} file and temp directory.
	Parallel int `yaml:"parallel,omitempty" json:"parallel,omitempty" toml:"parallel,omitempty"`
	// StageGlobs narrows the files of a staged skill directory to the ones
	// matching a glob (relative to the skill directory; a pattern with no slash
	// matches at any depth, ** crosses directories). SKILL.md is always staged.
	// Unset stages every regular file of the directory.
	StageGlobs []string `yaml:"stage_globs,omitempty" json:"stage_globs,omitempty" toml:"stage_globs,omitempty"` //nolint:tagliatelle
	// StageExclude leaves out the files of a staged skill directory that match a
	// glob (same syntax as StageGlobs); it wins over StageGlobs. SKILL.md is
	// always staged.
	StageExclude []string `yaml:"stage_exclude,omitempty" json:"stage_exclude,omitempty" toml:"stage_exclude,omitempty"` //nolint:tagliatelle
	// CacheFiles lists globs, relative to the project root, of files the scanner
	// reads besides the staged content (a rules file, a config). Their content is
	// part of the result cache key. The script a command runs through an
	// interpreter (python3 scan.py) is hashed without being listed.
	CacheFiles []string `yaml:"cache_files,omitempty" json:"cache_files,omitempty" toml:"cache_files,omitempty"` //nolint:tagliatelle
	// FailOn is the lowest severity of this scanner's findings that fails the run
	// ("error", "warning" or "info"). It replaces [lint.scanner_policy] fail_on for
	// this scanner, and the run's own threshold (--strict, --fail-on) for its findings.
	FailOn string `yaml:"fail_on,omitempty" json:"fail_on,omitempty" toml:"fail_on,omitempty"` //nolint:tagliatelle
	// MaxMessage is the number of characters of a finding message kept in text
	// output (default 500, at most 100000). The full text stays in the JSON
	// report and SARIF output. It wins over [lint.scanner_policy] max_message.
	MaxMessage int `yaml:"max_message,omitempty" json:"max_message,omitempty" toml:"max_message,omitempty"` //nolint:tagliatelle
	// MessageProperties lists SARIF result property paths (dotted, for example
	// "evidence" or "snippet") whose values are appended to the finding message as
	// "[name: value; ...]", counted against max_message. "location.snippet" reads
	// the standard locations[0].physicalLocation.region.snippet.text. Values are
	// sanitized like the message. SARIF format only.
	MessageProperties []string `yaml:"message_properties,omitempty" json:"message_properties,omitempty" toml:"message_properties,omitempty"` //nolint:tagliatelle
	// Required makes a missing, outdated or failing scanner an error instead of a notice.
	Required bool `yaml:"required,omitempty" json:"required,omitempty" toml:"required,omitempty"`
}

LintExternal is an external scanner. Command is an argv, run from the project root with the paths of the scanned files appended; it must print SARIF 2.1.0 or the ai-rulez JSON list to stdout. A relative command[0] is resolved against the project root, but with inputs the scanner's working directory is the stage, so a relative script argument is not: write it as {project}/tools/scan.py. It never runs through a shell, and every run is bounded by a timeout, an output cap and a process group kill.

type LintIgnoreRule added in v5.5.3

type LintIgnoreRule struct {
	// Rules lists finding codes (AR901) or names (claude-md-shadow); a pattern
	// with a wildcard ("AR9*") matches codes.
	Rules []string `yaml:"rules" json:"rules" toml:"rules"`
	// Paths lists globs, relative to the config directory or the repository
	// root, of the source files the ignore applies to.
	Paths []string `yaml:"paths" json:"paths" toml:"paths"`
	// Reason says why the findings are acceptable; it is for the reader.
	Reason string `yaml:"reason,omitempty" json:"reason,omitempty" toml:"reason,omitempty"`
}

LintIgnoreRule is one rule-scoped ignore: findings of Rules in files matching Paths are dropped, every other finding in those files is kept.

type LintMetadataRule

type LintMetadataRule struct {
	// Type is "string" (default), "date" or "enum".
	Type string `yaml:"type,omitempty" json:"type,omitempty" toml:"type,omitempty"`
	// Values lists the accepted values of an enum.
	Values []string `yaml:"values,omitempty" json:"values,omitempty" toml:"values,omitempty"`
	// MaxAgeDays reports a date older than this many days as stale (date only).
	MaxAgeDays int `yaml:"max_age_days,omitempty" json:"max_age_days,omitempty" toml:"max_age_days,omitempty"` //nolint:tagliatelle
	// Kinds limits the rule to content kinds (rule, context, skill, agent, command); empty means all.
	Kinds []string `yaml:"kinds,omitempty" json:"kinds,omitempty" toml:"kinds,omitempty"`
	// Required reports an item of the configured kinds that lacks the key.
	Required bool `yaml:"required,omitempty" json:"required,omitempty" toml:"required,omitempty"`
}

LintMetadataRule types one frontmatter key.

type LintRisk

type LintRisk struct {
	Error   *int `yaml:"error,omitempty" json:"error,omitempty" toml:"error,omitempty"`
	Warning *int `yaml:"warning,omitempty" json:"warning,omitempty" toml:"warning,omitempty"`
	Info    *int `yaml:"info,omitempty" json:"info,omitempty" toml:"info,omitempty"`
}

LintRisk weights the advisory risk score: the points one finding of each severity adds (capped at 100 per item or bundle). Unset keeps the default (error 25, warning 8, info 1); 0 is a valid weight.

type LintScannerPolicy

type LintScannerPolicy struct {
	// Preset adds a set of embedded scanner profiles to the entries of
	// [[lint.external]]: "off" (default), "baseline" or "strict". A preset never
	// contains a scanner that can send content off the machine.
	Preset string `yaml:"preset,omitempty" json:"preset,omitempty" toml:"preset,omitempty"`
	// Required lists scanner names whose absence or failure is an error.
	Required []string `yaml:"required,omitempty" json:"required,omitempty" toml:"required,omitempty"`
	// FailOn is the lowest severity of a scanner finding that makes the run fail:
	// "error", "warning" or "info". It applies to scanner findings only, and replaces the run's own
	// threshold (--strict, --fail-on) for them; a scanner entry's fail_on wins.
	FailOn string `yaml:"fail_on,omitempty" json:"fail_on,omitempty" toml:"fail_on,omitempty"` //nolint:tagliatelle
	// MaxMessage is the default number of characters of a scanner finding message
	// kept in text output (default 500, at most 100000); a scanner's own
	// max_message wins. The JSON report and SARIF output keep the full text.
	MaxMessage int `yaml:"max_message,omitempty" json:"max_message,omitempty" toml:"max_message,omitempty"` //nolint:tagliatelle
	// MaxTimeout is the longest timeout one run of a scanner may be given, a Go
	// duration up to 6h (default 15m). Per-scanner timeout values above it are
	// refused. Only the policy can raise it: an entry cannot lift its own bound.
	MaxTimeout string `yaml:"max_timeout,omitempty" json:"max_timeout,omitempty" toml:"max_timeout,omitempty"` //nolint:tagliatelle
	// Baseline is the scanner baseline file, relative to the project root
	// (default: scanner-baseline.json in the configuration directory).
	Baseline string `yaml:"baseline,omitempty" json:"baseline,omitempty" toml:"baseline,omitempty"`
	// Isolation is "auto" (default: confine an egress = false scanner when a
	// backend works), "require" (refuse to run it unconfined) or "none".
	Isolation string `yaml:"isolation,omitempty" json:"isolation,omitempty" toml:"isolation,omitempty"`
	// AllowEgress lists the scanners --allow-egress may enable. Unset, the flag
	// alone decides; set (even empty), a scanner outside the list never runs
	// with egress. Presets never read it.
	AllowEgress []string `yaml:"allow_egress,omitempty" json:"allow_egress,omitempty" toml:"allow_egress,omitempty"` //nolint:tagliatelle
}

LintScannerPolicy is [lint.scanner_policy].

type LintSecretPattern

type LintSecretPattern struct {
	Name  string `yaml:"name" json:"name" toml:"name"`
	Regex string `yaml:"regex" json:"regex" toml:"regex"`
}

LintSecretPattern is a named secret detector.

type LintSecurity

type LintSecurity struct {
	// ScanImports also scans content imported through includes and installed
	// skills, and makes `generate` refuse to write it when it has an
	// error-level finding. Unset (the default) scans and keeps each finding's own
	// severity; "error" or "warn" replaces the severity of every finding in
	// imported content; "off" opts out.
	ScanImports string `yaml:"scan_imports,omitempty" json:"scan_imports,omitempty" toml:"scan_imports,omitempty"` //nolint:tagliatelle
	// AllowedHosts restricts the hosts URLs may point to ("example.com", "*.example.com").
	// Empty disables the outbound host check.
	AllowedHosts []string `yaml:"allowed_hosts,omitempty" json:"allowed_hosts,omitempty" toml:"allowed_hosts,omitempty"` //nolint:tagliatelle
	// AllowedTools lists allowed-tools entries that may be unrestricted (for example "Bash").
	AllowedTools []string `yaml:"allowed_tools,omitempty" json:"allowed_tools,omitempty" toml:"allowed_tools,omitempty"` //nolint:tagliatelle
	// SecretPatterns adds regular expressions to the built-in secret detectors.
	SecretPatterns []LintSecretPattern `yaml:"secret_patterns,omitempty" json:"secret_patterns,omitempty" toml:"secret_patterns,omitempty"` //nolint:tagliatelle
	// InjectionPhrases adds case-insensitive phrases to the prompt-injection detector.
	InjectionPhrases []string `yaml:"injection_phrases,omitempty" json:"injection_phrases,omitempty" toml:"injection_phrases,omitempty"` //nolint:tagliatelle
	// DirectiveTags adds element names (for example "assistant") to the tags the
	// fake-directive-tag check (AR018) treats as imitating a privileged message,
	// next to the built-in <system> and <override>.
	DirectiveTags []string `yaml:"directive_tags,omitempty" json:"directive_tags,omitempty" toml:"directive_tags,omitempty"` //nolint:tagliatelle
	// TrustedOrgs replaces the built-in list of organizations (repository
	// owners) a skill or include may call itself official, verified or trusted
	// for (AR033). Unset keeps the built-in list.
	TrustedOrgs []string `yaml:"trusted_orgs,omitempty" json:"trusted_orgs,omitempty" toml:"trusted_orgs,omitempty"` //nolint:tagliatelle
}

LintSecurity configures the security checks. Everything is optional.

type LintTraps

type LintTraps struct {
	// ExtraHarnesses names harnesses whose traps run although no preset for them
	// is configured, for hand-written files such as .cursor/rules.
	ExtraHarnesses []string `yaml:"extra_harnesses,omitempty" json:"extra_harnesses,omitempty" toml:"extra_harnesses,omitempty"` //nolint:tagliatelle
	// MaxTableAgeDays reports (AR9C0) a trap or limits row whose verified_on is
	// older than this many days. 0, the default, turns the check off.
	MaxTableAgeDays int `yaml:"max_table_age_days,omitempty" json:"max_table_age_days,omitempty" toml:"max_table_age_days,omitempty"` //nolint:tagliatelle
}

LintTraps configures the checks for files a harness silently ignores.

type ListingSpec

type ListingSpec struct {
	// Skills, Commands and Agents say which generated item kinds the harness
	// lists. Claude Code merges commands into the skill listing and lists agents
	// in the Agent tool description, so it sets all three.
	Skills   bool `toml:"skills,omitempty" yaml:"skills,omitempty" json:"skills,omitempty"`
	Commands bool `toml:"commands,omitempty" yaml:"commands,omitempty" json:"commands,omitempty"`
	Agents   bool `toml:"agents,omitempty" yaml:"agents,omitempty" json:"agents,omitempty"`
	// IncludePath is true when each listing entry also carries the item's file
	// path (Codex and pi do).
	IncludePath bool `toml:"include_path,omitempty" yaml:"include_path,omitempty" json:"include_path,omitempty"`
	// DescriptionLimit is the harness's per-entry description truncation limit in
	// characters. Zero means none is known.
	DescriptionLimit int `toml:"description_limit,omitempty" yaml:"description_limit,omitempty" json:"description_limit,omitempty"`
}

ListingSpec declares what an agent harness puts in the prompt at session start to advertise the skills, commands and agents it can load. A harness that lists an item pays for its name and description (plus per-entry framing) on every request, whether or not the item is ever used.

Builtin providers described by the provider DSL declare it in their spec (`[listing]`); the hand-written presets are covered by builtinListings below. A zero ListingSpec lists nothing.

func BuiltinListing

func BuiltinListing(preset string) (ListingSpec, bool)

BuiltinListing returns the listing model of a hand-written preset.

func (ListingSpec) Any

func (s ListingSpec) Any() bool

Any reports whether the harness lists at least one item kind.

func (ListingSpec) Lists

func (s ListingSpec) Lists(kind OutputKind) bool

Lists reports whether the harness lists items of the given output kind.

type LoadOption

type LoadOption func(*loadOptions)

LoadOption customizes how a configuration is loaded.

func WithCollector

func WithCollector(c *diag.Collector) LoadOption

WithCollector makes the load keep its warning state in c instead of a fresh collector, and say each warning once for the life of c. A caller that loads the same project over and over (a watch loop, a live reload) passes one collector that outlives the loads, so a warning about the project is not repeated on every cycle. Loads that must not share state keep their own collector (the default).

func WithFrontmatterErrors

func WithFrontmatterErrors() LoadOption

WithFrontmatterErrors tells the load that the caller reports malformed frontmatter as an error (Config.Validate names the files), so the load does not print a warning for each file just before. Commands that do not validate keep the warning.

func WithHost

func WithHost(h ambient.Host) LoadOption

WithHost loads the configuration with the given environment, clock, process runner and logger instead of the real process's. The host is kept on the loaded Config (Config.Host), so generation and includes use it too. Fields left nil keep the real facility; a runner given by WithRunner is overridden by a non-nil host.Runner.

func WithIncludeMemo

func WithIncludeMemo(memo any) LoadOption

WithIncludeMemo makes the loaded config share an include fetch cache created by an earlier load (Config.IncludeMemo), so sources are fetched once per run.

func WithLayers added in v5.0.5

func WithLayers(names ...string) LoadOption

WithLayers activates the config.<name>.toml layers, in order, instead of the ones AI_RULEZ_LAYERS and AI_RULEZ_PROFILE name. Called with no names it activates none.

func WithLockPolicy

func WithLockPolicy(p LockPolicy) LoadOption

WithLockPolicy loads the configuration under p (see LockPolicy). The loaded Config keeps it (Config.LockPolicy), so reloads of the same project and the resolvers see the same policy.

func WithPolicy

func WithPolicy(e PolicyEnforcer) LoadOption

WithPolicy loads the configuration under the policy e: it clamps the loaded configuration, and the loaded Config keeps it for the questions asked later (PolicyLocksIn, nested loads). A nil e is no policy.

func WithPolicyDir

func WithPolicyDir(dir string) LoadOption

WithPolicyDir names the project directory the organization policy is discovered from, when the configuration is loaded from somewhere else, such as a snapshot of an earlier revision extracted to a temporary directory.

func WithRegistry

func WithRegistry(r *Registry) LoadOption

WithRegistry loads the configuration with the preset registry that validation and generation resolve presets through.

func WithResolvers

func WithResolvers(r Resolvers) LoadOption

WithResolvers supplies the functions that resolve includes and installed skills (see Resolvers). A load without them fails when the configuration declares includes or installed skills, unless WithoutRemote is given.

func WithRunner

func WithRunner(r runner.Runner) LoadOption

WithRunner makes every external command the load starts (git, for the .gitignore-aware skill bundling and for include fetches) go through r instead of running a real process. A nil r keeps the default (runner.Exec).

func WithUserScope added in v5.5.0

func WithUserScope() LoadOption

WithUserScope loads the user config: the paths it names (local includes and installed skills) may leave the project, as the user's own choice. It is set before includes and installed skills resolve, so they see the scope.

func WithWorkspace

func WithWorkspace(ws workspace.Workspace) LoadOption

WithWorkspace loads the project from ws instead of from the real directory baseDir names: every config, content and include read goes through it, so the project can live in memory or in a repository snapshot. baseDir may then be relative to ws's root, and the process working directory is never consulted.

func WithoutLayers added in v5.2.0

func WithoutLayers() LoadOption

WithoutLayers skips every config.<name>.toml layer, the default_layers named in config.toml included. Use it with WithoutLocal for anything that reads config.toml in order to modify and save it: a layered Config is a merged view and is never written back.

func WithoutLocal

func WithoutLocal() LoadOption

WithoutLocal skips machine-local inputs: the config.local.* overlay and the .ai-rulez/local/ content tree. Use it for anything that reads the shared configuration in order to modify and save it, and to reproduce the view a teammate without local overrides sees.

func WithoutRemote

func WithoutRemote() LoadOption

WithoutRemote skips resolving includes and installed skills: no remote is fetched and no cache is read. Use it for commands that only inspect the declared configuration, such as verifying ai-rulez.lock against it.

type LocalDoc

type LocalDoc struct {
	// Path is the overlay file path (existing, or where Save will create it).
	Path string
	// Doc is the overlay document being edited.
	Doc map[string]any
	// contains filtered or unexported fields
}

LocalDoc is an editable machine-local config.local.* document. Edits happen on the generic document so unrelated keys survive; Save validates the merged result before keeping the change.

func OpenLocalDoc

func OpenLocalDoc(configDir, mainConfigFile string) (*LocalDoc, error)

OpenLocalDoc opens the config.local.* file in configDir for editing, or starts an in-memory one in the same format as the main config (TOML by default). It holds an advisory lock (<configDir>/.config.local.lock) until Close so two concurrent edits cannot interleave; callers must defer Close.

func OpenLocalDocAt

func OpenLocalDocAt(configDir string) (*LocalDoc, error)

OpenLocalDocAt is OpenLocalDocInDir for an explicit config directory.

func OpenLocalDocInDir

func OpenLocalDocInDir(baseDir string) (*LocalDoc, error)

OpenLocalDocInDir opens the overlay for editing, holding the overlay's advisory lock until Close. Callers must defer Close.

func ViewLocalDoc

func ViewLocalDoc(baseDir string) (*LocalDoc, error)

ViewLocalDoc opens the overlay read-only, without taking the lock.

func ViewLocalDocAt

func ViewLocalDocAt(configDir string) (*LocalDoc, error)

ViewLocalDocAt is ViewLocalDoc for an explicit config directory.

func (*LocalDoc) Close

func (d *LocalDoc) Close()

Close releases the advisory lock taken by OpenLocalDoc. It is safe to call more than once and on a read-only doc.

func (*LocalDoc) Exists

func (d *LocalDoc) Exists() bool

Exists reports whether the overlay file is on disk.

func (*LocalDoc) HasLocalNamed

func (d *LocalDoc) HasLocalNamed(list, name string) bool

HasLocalNamed reports whether the overlay has a (non-removing) entry list/name.

func (*LocalDoc) HasLocalProfile

func (d *LocalDoc) HasLocalProfile(name string) bool

HasLocalProfile reports whether the overlay itself defines the profile.

func (*LocalDoc) RemoveNamed

func (d *LocalDoc) RemoveNamed(list, name string, sharedHas bool) error

RemoveNamed drops the local entry list/name. When the shared config also has it, a {name, remove = true} entry is written so the shared one is deleted.

func (*LocalDoc) RemoveProfile

func (d *LocalDoc) RemoveProfile(name string) error

RemoveProfile deletes a profile from the overlay.

func (*LocalDoc) Save

func (d *LocalDoc) Save(ctx context.Context) error

Save writes the overlay (owner-only, atomically), then validates it against the local schema and loads the merged configuration from disk to validate that. If anything fails the previous file content is restored and the error returned. The ignore entries are ensured before anything is written, so a secret-bearing overlay is never on disk unignored. Includes are not fetched for the validation load.

func (*LocalDoc) Set

func (d *LocalDoc) Set(path []string, v any) error

Set stores v at the key path (see ParseLocalPath).

func (*LocalDoc) SetDefault

func (d *LocalDoc) SetDefault(name string) error

SetDefault sets the local default profile.

func (*LocalDoc) SetProfile

func (d *LocalDoc) SetProfile(name string, domains []string) error

SetProfile defines (or replaces) a local profile.

func (*LocalDoc) Unset

func (d *LocalDoc) Unset(path []string) error

Unset removes the value at the key path from the overlay. For a named list, "<list>.<name>" removes the whole local entry.

func (*LocalDoc) UpsertNamed

func (d *LocalDoc) UpsertNamed(list, name string, fields map[string]any) error

UpsertNamed creates or updates the local entry list/name with the given fields. If the entry is a local remove marker it is replaced, not extended.

func (*LocalDoc) WithLog

func (d *LocalDoc) WithLog(log logger.Logger) *LocalDoc

WithLog makes Save report to log instead of the CLI's logger.

func (*LocalDoc) WithResolvers

func (d *LocalDoc) WithResolvers(r Resolvers) *LocalDoc

WithResolvers makes the validation load of Save resolve includes and installed skills the way the caller's own loads do.

type LocalOverlay

type LocalOverlay struct {
	// Path is the absolute path of the overlay file.
	Path string
	// Doc is the normalized overlay document as decoded from disk. Treat it as
	// read-only.
	Doc map[string]any
	// Tracked is true when git tracks the overlay file. A committed overlay came
	// from the repository, not from this machine, so it gets no machine-local trust.
	Tracked bool
}

LocalOverlay records the machine-local config.local.* document merged into a loaded Config. A Config carrying one is a merged view and must never be written back as the shared configuration.

func ReadLocalOverlay

func ReadLocalOverlay(configDir string) (*LocalOverlay, error)

ReadLocalOverlay reads config.local.toml beside the main config in configDir without loading the rest of the configuration. It returns nil when there is none.

func (*LocalOverlay) KeyPaths

func (o *LocalOverlay) KeyPaths() []string

KeyPaths lists the dotted key paths the overlay sets, sorted, without any values (overlay files may hold secrets). Named list entries are addressed by name, e.g. "mcp_servers.github.env.TOKEN".

type LocalRootProvider

type LocalRootProvider interface {
	LocalRootFile() string
}

LocalRootProvider is implemented by preset generators that emit a single markdown root instructions file and therefore support a machine-local ".local" variant of it (CLAUDE.md → CLAUDE.local.md). LocalRootFile returns the local variant's path relative to the output base dir, or "" when the preset has no single-file markdown root (e.g. cursor, devin) and so has no local variant.

type LocalRootRenderer

type LocalRootRenderer interface {
	RenderLocalRoot(local *ContentTree, rules []ContentFile, baseDir string, cfg *Config) (OutputFile, error)
}

LocalRootRenderer is implemented by presets whose machine-local root file is not the generic markdown override (Copilot writes a path-specific instructions file with an applyTo frontmatter). RenderLocalRoot builds that file from the local rules the preset keeps inline and the local context; local is the profile-selected local content tree.

type LocalRootStandIn

type LocalRootStandIn interface {
	LocalRootStandsIn() string
}

LocalRootStandIn is implemented by LocalRootProviders whose local file is not the ".local" variant of the root file it stands in for (a file in a rules folder). LocalRootStandsIn returns that root file, which frontmatter targets are matched against.

type LocalRuleProvider

type LocalRuleProvider interface {
	LocalRuleOutputs(rules []ContentFile, baseDir string, cfg *Config) (files []OutputFile, inline []ContentFile, err error)
}

LocalRuleProvider is implemented by preset generators that write one native rule file per rule. LocalRuleOutputs plans machine-local rules with the same routing the preset applies to shared rules and renders the ones it routes to rule files as personal overrides, "<rulesdir>/<id>.local<ext>", which the tool loads natively. The outputs are LocalOnly. Rules the routing keeps inline are returned in inline for the local root file. Local rule IDs never contain a path separator (local rules are not scoped).

type LockConfig

type LockConfig struct {
	// Enforce makes `validate` report content drift (AR981, AR982),
	// makes `lock --check` fail on a lock without content pins, makes `generate`
	// refuse a remote include or installed skill the lock does not pin (AR010 is
	// an error), and makes the skills server refuse a served skill whose digest
	// is not the one recorded in ai-rulez.lock. Unset means true when
	// ai-rulez.lock exists and false otherwise; false opts out.
	Enforce *bool `yaml:"enforce,omitempty" json:"enforce,omitempty" toml:"enforce,omitempty"`
	// IncludeOutputs also pins digests of the generated files. Default true.
	IncludeOutputs *bool `yaml:"include_outputs,omitempty" json:"include_outputs,omitempty" toml:"include_outputs,omitempty"` //nolint:tagliatelle
	// Scope is "all" (default) or "skills".
	Scope string `yaml:"scope,omitempty" json:"scope,omitempty" toml:"scope,omitempty"`
	// MinReleaseAge is the default for sources that use a version constraint and
	// set none of their own ("7d"). A tag younger than this is held back by
	// `lock` and `update` (AR733).
	MinReleaseAge string `yaml:"min_release_age,omitempty" json:"min_release_age,omitempty" toml:"min_release_age,omitempty"` //nolint:tagliatelle
	// MinReleaseAgeSource is where a tag's release time comes from: "auto"
	// (default: the forge, else the first time this machine saw the tag, else the
	// commit date), "forge", "first-seen" or "commit".
	MinReleaseAgeSource string `yaml:"min_release_age_source,omitempty" json:"min_release_age_source,omitempty" toml:"min_release_age_source,omitempty"` //nolint:tagliatelle
	// VerifyTags makes `generate` and `lock --check` ask the remotes whether a
	// tag pinned in ai-rulez.lock moved (AR732) or was deleted (AR735). It needs
	// the network, so it is off by default and the offline checks stay offline.
	VerifyTags bool `yaml:"verify_tags,omitempty" json:"verify_tags,omitempty" toml:"verify_tags,omitempty"` //nolint:tagliatelle
	// RefreshOnGenerate makes `generate` re-resolve remote includes, installed
	// skills and skill sources to their latest allowed version and rewrite
	// ai-rulez.lock before it writes outputs, as `generate --refresh-remote` does.
	RefreshOnGenerate bool `yaml:"refresh_on_generate,omitempty" json:"refresh_on_generate,omitempty" toml:"refresh_on_generate,omitempty"` //nolint:tagliatelle
}

LockConfig is the [lock] table: how strictly the committed ai-rulez.lock is enforced. Enforcement is on whenever ai-rulez.lock exists; `enforce = false` opts out.

type LockMode

type LockMode int

LockMode says how a load resolves remote sources against ai-rulez.lock.

const (
	// LockAuto uses the lock when one covers a source and fetches an uncovered
	// source as before. Without a lock file nothing changes.
	LockAuto LockMode = iota
	// LockRequire (generate --locked) fails when the lock is missing or does not
	// cover a configured remote source.
	LockRequire
	// LockFrozen (generate --frozen) is LockRequire that never touches the
	// network; set Offline with it.
	LockFrozen
	// LockRefresh (ai-rulez lock) re-resolves the sources Refresh selects from
	// the remote, ignoring their pins, so new pins can be recorded.
	LockRefresh
)

type LockPolicy

type LockPolicy struct {
	Mode LockMode
	// Refresh selects what LockRefresh re-resolves; nil selects everything.
	Refresh func(kind, name string) bool
	// Offline resolves remote sources from the local cache only (--no-fetch,
	// --frozen): the load never touches the network.
	Offline bool
	// RequireWhenEnforced makes an enforced lock ([lock] enforce, on by default
	// when ai-rulez.lock exists) behave like LockRequire: a remote source the
	// lock does not cover is a violation instead of an unpinned fetch. Every
	// writer of outputs sets it (generate, the MCP generate_outputs tool, the
	// pkg/airulez facade); commands that only read or report leave it off.
	RequireWhenEnforced bool
	// VersionPolicy is how a refresh moves the sources that ask for a version
	// range (`ai-rulez update`, and the release age gate of `lock`).
	VersionPolicy
}

LockPolicy is how one load treats ai-rulez.lock and the network (WithLockPolicy). It belongs to the load, not to the process: two loads in one process (an MCP server, an embedding service) each get their own. The zero value fetches what the lock does not cover and only reports an enforced lock; a load that writes outputs sets RequireWhenEnforced.

func (LockPolicy) Refreshing

func (p LockPolicy) Refreshing(kind, name string) bool

Refreshing reports whether the source is being re-resolved by `ai-rulez lock`.

func (LockPolicy) Strict

func (p LockPolicy) Strict(enforced bool) bool

Strict reports whether a source that cannot be resolved must fail the load instead of being skipped with a warning: --locked, --frozen, or an enforced lock under a policy that requires it.

type MCPConfig

type MCPConfig struct {
	// SelfServer makes generation add ai-rulez's own MCP server
	// (`npx -y ai-rulez@latest mcp`) to the project .mcp.json. It defaults to
	// true in v5, so a project gets the server out of the box; set it to false
	// (or pass generate --no-self-mcp) to turn it off. The entry is merged into
	// the file, so hand-authored servers beside it survive, and no other MCP
	// output (.claude/settings.json, ...) is touched.
	SelfServer *bool `yaml:"self_server,omitempty" json:"self_server,omitempty" toml:"self_server,omitempty"`

	// SelfServerVersion pins the ai-rulez version the entry runs. Empty means
	// "latest", so a project picks up new releases without regenerating.
	SelfServerVersion string `yaml:"self_server_version,omitempty" json:"self_server_version,omitempty" toml:"self_server_version,omitempty"`

	// SelfServerCommand replaces the whole launch command (executable followed
	// by its arguments), for installs that do not run through npx, for example
	// ["ai-rulez", "mcp"]. SelfServerVersion is ignored when it is set.
	SelfServerCommand []string `yaml:"self_server_command,omitempty" json:"self_server_command,omitempty" toml:"self_server_command,omitempty"`

	// SelfServerLauncher chooses how the self entry starts ai-rulez: "npx" (the
	// default) runs it through npx, "binary-then-npx" runs an installed `ai-rulez`
	// when one is on PATH and falls back to npx. SelfServerVersion pins the npx
	// fallback. It cannot be combined with SelfServerCommand.
	SelfServerLauncher string `yaml:"self_server_launcher,omitempty" json:"self_server_launcher,omitempty" toml:"self_server_launcher,omitempty"`

	// SkillsInstructions, when false, leaves the "Unlisted skills" section out of
	// the instruction files. Nil means on: while skills are served, every
	// instruction file tells the agent to call find_skill and load_skill.
	SkillsInstructions *bool `yaml:"skills_instructions,omitempty" json:"skills_instructions,omitempty" toml:"skills_instructions,omitempty"`

	// SkillsInstructionsText replaces the default section. {{server}} is the
	// MCP server that serves the skills and {{skills_dir}} the config directory's
	// skills folder, relative to the instruction file.
	SkillsInstructionsText string `yaml:"skills_instructions_text,omitempty" json:"skills_instructions_text,omitempty" toml:"skills_instructions_text,omitempty"`

	// DiscoverSkills makes `mcp --serve-skills` also serve the skills already in
	// the repository: "off" (the default), "repo" (skill directories of the working
	// tree) or "all" (repo plus installed and included skills). The
	// --discover-skills flag overrides it.
	DiscoverSkills string `yaml:"discover_skills,omitempty" json:"discover_skills,omitempty" toml:"discover_skills,omitempty"`

	// IndexCache says where the repository index of the discovery keeps its
	// cache: "user" (the default; the user cache directory, with a gitignored
	// directory in the project as the fallback when it is not writable), "repo"
	// (that project directory) or "off". The cache holds summaries and scan
	// verdicts, never file bodies.
	IndexCache string `yaml:"index_cache,omitempty" json:"index_cache,omitempty" toml:"index_cache,omitempty"`

	// ServeSkills makes a bare `ai-rulez mcp` behave as `mcp --serve-skills`, so
	// a client entry needs no flag. The --serve-skills flag wins, including
	// --serve-skills=false.
	ServeSkills *bool `yaml:"serve_skills,omitempty" json:"serve_skills,omitempty" toml:"serve_skills,omitempty"`

	// ReadOnly registers only the read-only tools, as `mcp --read-only` does. An
	// organization policy's mcp.read_only = true cannot be loosened by it.
	ReadOnly *bool `yaml:"read_only,omitempty" json:"read_only,omitempty" toml:"read_only,omitempty"`

	// Daemon shares one long-lived skills server per repository, as
	// `mcp --serve-skills --daemon` does.
	Daemon *bool `yaml:"daemon,omitempty" json:"daemon,omitempty" toml:"daemon,omitempty"`

	// DaemonIdleTimeout is how long the shared daemon outlives its last client,
	// as a Go duration ("30s", "5m"); empty keeps the 15 minute default.
	DaemonIdleTimeout string `yaml:"daemon_idle_timeout,omitempty" json:"daemon_idle_timeout,omitempty" toml:"daemon_idle_timeout,omitempty"`

	// Authoring keeps the authoring tools on a read-only skills server. Without
	// it, `mcp --serve-skills` with read_only registers only the skill-serving
	// tools (find_skill, load_skill, search_skills, read_skill_file,
	// list_skill_resources) instead of the whole authoring set. A `tools`
	// list wins over both.
	Authoring *bool `yaml:"authoring,omitempty" json:"authoring,omitempty" toml:"authoring,omitempty"`

	// NoWatch stops `mcp --serve-skills` reloading when skill files change, as
	// --no-watch does.
	NoWatch *bool `yaml:"no_watch,omitempty" json:"no_watch,omitempty" toml:"no_watch,omitempty"`

	// Allow and Deny are the --allow and --deny name globs of the served skills.
	Allow []string `yaml:"allow,omitempty" json:"allow,omitempty" toml:"allow,omitempty"`
	Deny  []string `yaml:"deny,omitempty" json:"deny,omitempty" toml:"deny,omitempty"`

	// Tools, when set, is the only list of tools the server registers (the
	// --tools flag), for example ["find_skill", "load_skill", "find_context",
	// "load_context"] for a server that can only read.
	Tools []string `yaml:"tools,omitempty" json:"tools,omitempty" toml:"tools,omitempty"`

	// Exclude lists globs, relative to the project root, of the paths repository
	// discovery (`mcp --serve-skills --discover-skills`) never reads: skill
	// directories and the context index alike. The --exclude flag adds to it.
	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty" toml:"exclude,omitempty"`
}

MCPConfig holds project-level MCP generation options, as opposed to the individual server definitions in [[mcp_servers]].

func (*MCPConfig) ShowSkillsInstructions added in v5.0.4

func (m *MCPConfig) ShowSkillsInstructions() bool

ShowSkillsInstructions reports whether the skills section may be written; it is on unless [mcp] skills_instructions = false.

type MCPOAuth added in v5.0.2

type MCPOAuth struct {
	ClientID     string   `yaml:"client_id,omitempty" json:"client_id,omitempty" toml:"client_id,omitempty"`             //nolint:tagliatelle
	CallbackPort int      `yaml:"callback_port,omitempty" json:"callback_port,omitempty" toml:"callback_port,omitempty"` //nolint:tagliatelle
	Scopes       []string `yaml:"scopes,omitempty" json:"scopes,omitempty" toml:"scopes,omitempty"`
}

MCPOAuth is the pre-registered OAuth client a hosted (http or sse) MCP server authenticates with: the client ID registered with the provider, the local callback port it was registered for, and the scopes to request. A harness that reads an `oauth` block renders the fields it understands.

func (*MCPOAuth) IsZero added in v5.0.2

func (o *MCPOAuth) IsZero() bool

IsZero reports whether the block carries no OAuth configuration at all.

type MCPServer

type MCPServer struct {
	Name        string            `yaml:"name" json:"name" toml:"name"`
	Description string            `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	Command     string            `yaml:"command,omitempty" json:"command,omitempty" toml:"command,omitempty"`
	Args        []string          `yaml:"args,omitempty" json:"args,omitempty" toml:"args,omitempty"`
	Env         map[string]string `yaml:"env,omitempty" json:"env,omitempty" toml:"env,omitempty"`
	Transport   string            `yaml:"transport,omitempty" json:"transport,omitempty" toml:"transport,omitempty"`
	// Type is the explicit transport written to a harness's `type` key
	// (stdio, http or sse). It is an alias of Transport: when both are set they
	// must agree, and an unset Type falls back to Transport.
	Type string `yaml:"type,omitempty" json:"type,omitempty" toml:"type,omitempty"`
	URL  string `yaml:"url,omitempty" json:"url,omitempty" toml:"url,omitempty"`
	// OAuth is the pre-registered OAuth client of a hosted (http/sse) server,
	// rendered into the harnesses whose MCP format has an `oauth` block. A
	// harness that cannot express it skips the server with a warning rather
	// than writing one that could not authenticate.
	OAuth *MCPOAuth `yaml:"oauth,omitempty" json:"oauth,omitempty" toml:"oauth,omitempty"`
	// Headers are HTTP headers sent to a remote (http/sse) server, typically for
	// auth. Values may contain ${VAR} placeholders, resolved like Env.
	Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty" toml:"headers,omitempty"`
	Enabled *bool             `yaml:"enabled,omitempty" json:"enabled,omitempty" toml:"enabled,omitempty"`
	// Profiles restricts this server to the named profiles. Empty means every
	// profile, preserving the pre-existing behavior.
	Profiles []string `yaml:"profiles,omitempty" json:"profiles,omitempty" toml:"profiles,omitempty"`
	// Domain restricts this server to the named domain: it is active only when
	// that domain is selected by the active profile or role. Empty means global,
	// preserving the pre-existing behavior. When both Domain and Profiles are
	// set, both must pass.
	Domain string `yaml:"domain,omitempty" json:"domain,omitempty" toml:"domain,omitempty"`
	// Package is the package URL (purl) of the package a command-based server
	// runs, for example "pkg:npm/%40scope/server@1.4.2". `ai-rulez sbom` uses it
	// instead of guessing the package from command and args. It is never written
	// to a harness file.
	Package string `yaml:"package,omitempty" json:"package,omitempty" toml:"package,omitempty"`

	// SecretEnvKeys records env keys whose generated values should be treated as
	// sensitive. It is populated during generation and never serialized.
	SecretEnvKeys []string `yaml:"-" json:"-" toml:"-"`
	// SecretHeaderKeys is the Headers counterpart of SecretEnvKeys.
	SecretHeaderKeys []string `yaml:"-" json:"-" toml:"-"`
	// EnvRefs and HeaderRefs map the Env and Headers keys whose value held a
	// ${VAR} placeholder to that value as written. They are populated during
	// generation and never serialized; a tool that expands environment references
	// itself (Codex, Cursor) is given the reference instead of the resolved secret.
	EnvRefs    map[string]string `yaml:"-" json:"-" toml:"-"`
	HeaderRefs map[string]string `yaml:"-" json:"-" toml:"-"`
}

func (MCPServer) Clone

func (m MCPServer) Clone() MCPServer

Clone returns a deep copy of the server. The generator resolves ${VAR} placeholders and ${PROJECT_ROOT} in place on the servers it holds, so the map built from MCPServersRaw must own its data: the as-written servers stay untouched and a digest of them never sees a resolved secret.

func (*MCPServer) GetTransport

func (m *MCPServer) GetTransport() string

GetTransport returns the transport protocol, defaulting to "stdio". An explicit Type wins over Transport so a config may spell the harness's `type` key directly.

func (*MCPServer) HasOAuth added in v5.0.2

func (m *MCPServer) HasOAuth() bool

HasOAuth reports whether the server declares a pre-registered OAuth client.

func (*MCPServer) IsEnabled

func (m *MCPServer) IsEnabled() bool

IsEnabled returns true if the MCP server is enabled (defaults to true if not specified)

type ManagedSettings

type ManagedSettings struct {
	// Env owns env.<NAME> for every listed variable.
	Env map[string]string `yaml:"env,omitempty" json:"env,omitempty" toml:"env,omitempty"`
	// SkillOverrides owns skillOverrides.<skill> for every listed skill. Values
	// are on, name-only, user-invocable-only or off.
	SkillOverrides map[string]string `yaml:"skill_overrides,omitempty" json:"skill_overrides,omitempty" toml:"skill_overrides,omitempty"` //nolint:tagliatelle
}

ManagedSettings is the [claude.settings.managed] block: keys of .claude/settings.json ai-rulez owns entry by entry. Unlike the plugin keys of [claude.settings] it needs no `manage = true`: declaring an entry is the opt-in.

func (*ManagedSettings) IsEmpty

func (m *ManagedSettings) IsEmpty() bool

IsEmpty reports whether no managed key is declared.

type ManifestDepMatch

type ManifestDepMatch struct {
	File    string `yaml:"file" json:"file" toml:"file"`
	Pattern string `yaml:"pattern" json:"pattern" toml:"pattern"`
}

ManifestDepMatch pairs a manifest path regex with a content regex.

type MarketplaceAuthoring

type MarketplaceAuthoring struct {
	Name        string  `yaml:"name" json:"name" toml:"name"`
	Description string  `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	Owner       *Author `yaml:"owner,omitempty" json:"owner,omitempty" toml:"owner,omitempty"`

	// Members lists sub-project directories for a multi-plugin monorepo. Empty
	// means single-plugin (marketplace source "./").
	Members []string `yaml:"members,omitempty" json:"members,omitempty" toml:"members,omitempty"`

	// OutputDir is the project-relative directory the domain-plugin marketplace
	// is written to (".claude-plugin/marketplace.json" and "plugins/<name>/"
	// beneath it). Empty means the project root. Used only when FromDomains or
	// Plugins is set.
	OutputDir string `yaml:"output_dir,omitempty" json:"output_dir,omitempty" toml:"output_dir,omitempty"` //nolint:tagliatelle

	// FromDomains turns every selected domain into its own plugin.
	FromDomains *DomainPluginsConfig `yaml:"from_domains,omitempty" json:"from_domains,omitempty" toml:"from_domains,omitempty"` //nolint:tagliatelle

	// Plugins declares plugins by hand, mixing domains and root content. An entry
	// named like a FromDomains plugin replaces it.
	Plugins []MarketplacePlugin `yaml:"plugins,omitempty" json:"plugins,omitempty" toml:"plugins,omitempty"`

	// CursorIndex also writes .cursor-plugin/marketplace.json next to the
	// Claude index for members and domain plugins (default off).
	CursorIndex bool `yaml:"cursor_index,omitempty" json:"cursor_index,omitempty" toml:"cursor_index,omitempty"` //nolint:tagliatelle

	// CatalogSkill generates a skill listing the plugins and how to enable them.
	CatalogSkill *CatalogSkillConfig `yaml:"catalog_skill,omitempty" json:"catalog_skill,omitempty" toml:"catalog_skill,omitempty"` //nolint:tagliatelle
}

MarketplaceAuthoring describes the marketplace index emitted for a plugin (or a set of plugins, for a monorepo). Distinct from the consumer MarketplaceConfig.

func (*MarketplaceAuthoring) HasDomainPlugins

func (m *MarketplaceAuthoring) HasDomainPlugins() bool

HasDomainPlugins reports whether the marketplace generates plugins from the domain tree (from_domains or [[marketplace.plugins]]).

type MarketplaceConfig

type MarketplaceConfig struct {
	Name   string `yaml:"name" json:"name" toml:"name"`
	Source string `yaml:"source" json:"source" toml:"source"`
	Type   string `yaml:"type" json:"type" toml:"type"` // "github", "git", "local", "url"
}

MarketplaceConfig represents a custom plugin marketplace

type MarketplaceDefinition added in v5.2.0

type MarketplaceDefinition struct {
	Name        string  `yaml:"name" json:"name" toml:"name"`
	Description string  `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	Owner       *Author `yaml:"owner,omitempty" json:"owner,omitempty" toml:"owner,omitempty"`
	// Plugins are the names of the plugin definitions ([[plugins_authoring]] or
	// [[marketplace.plugins]]) the index lists. An empty list lists nothing.
	Plugins []string `yaml:"plugins,omitempty" json:"plugins,omitempty" toml:"plugins,omitempty"`
	// OutputDir is the project-relative directory holding the index and its
	// plugins/<name>/ bundles. Empty means the project root. Unique per marketplace.
	OutputDir   string `yaml:"output_dir,omitempty" json:"output_dir,omitempty" toml:"output_dir,omitempty"`       //nolint:tagliatelle
	CursorIndex bool   `yaml:"cursor_index,omitempty" json:"cursor_index,omitempty" toml:"cursor_index,omitempty"` //nolint:tagliatelle
}

MarketplaceDefinition is one [[marketplaces_authoring]] entry: a marketplace index that lists plugin definitions by name. Several can come from one root, each with its own index and provenance sidecar. Distinct from the consumer MarketplaceConfig and from the single [marketplace] block.

type MarketplacePlugin

type MarketplacePlugin struct {
	Name        string `yaml:"name" json:"name" toml:"name"`
	Description string `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	// Domains are the domains (names or globs) whose skills, commands and agents
	// the plugin bundles.
	Domains []string `yaml:"domains,omitempty" json:"domains,omitempty" toml:"domains,omitempty"`
	// Skills, Commands and Agents select root content by name or glob.
	Skills   []string `yaml:"skills,omitempty" json:"skills,omitempty" toml:"skills,omitempty"`
	Commands []string `yaml:"commands,omitempty" json:"commands,omitempty" toml:"commands,omitempty"`
	Agents   []string `yaml:"agents,omitempty" json:"agents,omitempty" toml:"agents,omitempty"`
	// MCPServers names root [[mcp_servers]] the plugin bundles. Empty bundles none.
	MCPServers []string `yaml:"mcp_servers,omitempty" json:"mcp_servers,omitempty" toml:"mcp_servers,omitempty"` //nolint:tagliatelle
	// Hooks are the lifecycle hooks the plugin ships. Empty ships none.
	Hooks []HookGroup `yaml:"hooks,omitempty" json:"hooks,omitempty" toml:"hooks,omitempty"`
	// Author, License, Homepage and Repository fall back to the [plugin] block.
	Author     *Author `yaml:"author,omitempty" json:"author,omitempty" toml:"author,omitempty"`
	License    string  `yaml:"license,omitempty" json:"license,omitempty" toml:"license,omitempty"`
	Homepage   string  `yaml:"homepage,omitempty" json:"homepage,omitempty" toml:"homepage,omitempty"`
	Repository string  `yaml:"repository,omitempty" json:"repository,omitempty" toml:"repository,omitempty"`
	// Relevance tells Claude Code when to suggest the plugin.
	Relevance      *PluginRelevance `yaml:"relevance,omitempty" json:"relevance,omitempty" toml:"relevance,omitempty"`
	PluginDefaults `yaml:",inline"`
}

MarketplacePlugin is one [[marketplace.plugins]] entry.

type MarketplaceSource

type MarketplaceSource struct {
	Source string `yaml:"source" json:"source" toml:"source"` // directory, github, git, url
	Path   string `yaml:"path,omitempty" json:"path,omitempty" toml:"path,omitempty"`
	Repo   string `yaml:"repo,omitempty" json:"repo,omitempty" toml:"repo,omitempty"`
	URL    string `yaml:"url,omitempty" json:"url,omitempty" toml:"url,omitempty"`
	Ref    string `yaml:"ref,omitempty" json:"ref,omitempty" toml:"ref,omitempty"`
}

MarketplaceSource is a Claude Code marketplace source object.

type MarketplaceTarget added in v5.2.0

type MarketplaceTarget struct {
	Name        string
	Description string
	Owner       *Author
	// Plugins names the plugin definitions the index lists; nil lists every plugin
	// of the root.
	Plugins     []string
	OutputDir   string
	CursorIndex bool
	// Classic is the single [marketplace] block when it is the source of the
	// target, which also carries members, from_domains and the catalog skill.
	Classic *MarketplaceAuthoring
}

MarketplaceTarget is one marketplace index a root writes.

type MergeSource

type MergeSource struct {
	Path   string
	Format string
	Owned  []jsonmerge.OwnedKey
}

MergeSource is what an output was rendered from: the document (Path, the file the merge read), its format (a docmerge format, or MergeFormatOwnedHooks) and the keys ai-rulez owns in it.

type Metadata

type Metadata struct {
	Priority string   `yaml:"priority,omitempty" json:"priority,omitempty"`
	Targets  []string `yaml:"targets,omitempty" json:"targets,omitempty"`
	Aliases  []string `yaml:"aliases,omitempty" json:"aliases,omitempty"`
	Tools    []string `yaml:"tools,omitempty" json:"tools,omitempty"`
	Skills   []string `yaml:"skills,omitempty" json:"skills,omitempty"`
	Keywords []string `yaml:"keywords,omitempty" json:"keywords,omitempty"`
	Usage    string   `yaml:"usage,omitempty" json:"usage,omitempty"`
	Shortcut string   `yaml:"shortcut,omitempty" json:"shortcut,omitempty"`
	Category string   `yaml:"category,omitempty" json:"category,omitempty"`
	Effort   string   `yaml:"effort,omitempty" json:"effort,omitempty"`
	// Activation selects when a rule or context file applies: always, glob,
	// auto, or manual. See ResolveActivation for precedence and defaults.
	Activation string `yaml:"activation,omitempty" json:"activation,omitempty"`
	// Globs and Paths declare the files a rule applies to. They are two
	// spellings of the same idea (Cursor calls them globs, Claude Code calls
	// them paths); either populates the same path-scope used by presets.
	Globs []string          `yaml:"globs,omitempty" json:"globs,omitempty"`
	Paths []string          `yaml:"paths,omitempty" json:"paths,omitempty"`
	Extra map[string]string `yaml:",inline" json:",inline"`

	// OKFType and OKFTitle are the `type` and `title` an OKF concept declares.
	// They carry nothing for ai-rulez; the loader keeps them so that an export
	// of an OKF-shaped tree reproduces the type and title it was written with.
	OKFType  string `yaml:"-" json:"-"`
	OKFTitle string `yaml:"-" json:"-"`
	// contains filtered or unexported fields
}

Metadata represents parsed frontmatter metadata.

Tools, Skills, and Keywords are list-valued and need typed handling because YAML sequences cannot round-trip through map[string]string — they would be stringified via fmt %v ("[a b c]") instead of preserved as proper lists.

func ParseFrontmatterChecked

func ParseFrontmatterChecked(content string) (metadata *Metadata, body string, malformed bool)

ParseFrontmatterChecked is ParseFrontmatterPublic that also reports a delimited frontmatter block whose YAML could not be parsed (it is dropped from body), so the caller, which knows the file and its logger, can say so.

func ParseFrontmatterPublic

func ParseFrontmatterPublic(content string) (metadata *Metadata, body string)

ParseFrontmatterPublic is the exported version of parseFrontmatter for use by other packages

func (*Metadata) ActivationValue

func (m *Metadata) ActivationValue() ActivationMode

ActivationValue returns the declared `activation`, trimmed and lowercased.

func (*Metadata) ExecutingExtraKeys

func (m *Metadata) ExecutingExtraKeys() []string

ExecutingExtraKeys lists the frontmatter keys, as spelled, that execute or connect (see IsExecutingFrontmatterKey), sorted.

func (*Metadata) ExtraBool

func (m *Metadata) ExtraBool(key string) (value, set bool)

ExtraBool reports a boolean extra frontmatter key. set is false when the key is absent or not a recognized boolean. It accepts the spellings Claude Code documents (true/false, yes/no, on/off, 1/0, in any letter case).

func (*Metadata) ExtraKeysFor

func (m *Metadata) ExtraKeysFor(normalized string) []string

ExtraKeysFor lists the frontmatter keys, as spelled, whose normalized form is the given registry name (FrontmatterHooks, FrontmatterMCPServers), sorted.

func (*Metadata) ExtraList

func (m *Metadata) ExtraList(key string) []string

ExtraList returns a list-valued frontmatter key that has no typed field (such as `triggers`): a YAML sequence, or a single comma-separated scalar.

func (*Metadata) GetPriority

func (m *Metadata) GetPriority() Priority

GetPriority returns the priority as a Priority type, defaulting to medium

func (*Metadata) GetTriggerDescription

func (m *Metadata) GetTriggerDescription() string

GetTriggerDescription retrieves the description for model_decision mode

func (*Metadata) GetTriggerGlob

func (m *Metadata) GetTriggerGlob() string

GetTriggerGlob retrieves the glob pattern for glob mode

func (*Metadata) GetTriggerKeywords

func (m *Metadata) GetTriggerKeywords() []string

GetTriggerKeywords retrieves the trigger keywords for manual mode. Returns a copy of the typed Keywords slice (already sorted by the scanner).

func (*Metadata) GetTriggerMode

func (m *Metadata) GetTriggerMode() string

GetTriggerMode retrieves the trigger mode from metadata Returns "manual" as default if not specified or invalid

func (*Metadata) HasTargets

func (m *Metadata) HasTargets() bool

HasTargets returns true if targets are specified

func (*Metadata) PathScope

func (m *Metadata) PathScope() []string

PathScope returns the file globs a rule declares, from either `globs` or `paths` frontmatter. Empty means the rule is not path-scoped.

func (*Metadata) ResolveActivation

func (m *Metadata) ResolveActivation() Activation

ResolveActivation resolves when the item applies. Precedence: explicit `activation`, the `trigger` dialect (Devin, Antigravity), legacy Cursor `alwaysApply`, then derived (globs present means glob, otherwise always). A description alone never implies auto. Safe on a nil receiver (always).

func (*Metadata) ShouldRenderTriggerFrontmatter

func (m *Metadata) ShouldRenderTriggerFrontmatter() bool

ShouldRenderTriggerFrontmatter checks if trigger frontmatter should be rendered Returns true if trigger mode is non-default or has additional config

func (*Metadata) SkillSpecFields

func (m *Metadata) SkillSpecFields() map[string]any

SkillSpecFields returns the Agent Skills specification fields the skill sets (license, compatibility, metadata, allowed-tools) with their original types. The result is empty when none is set.

func (*Metadata) TypedExtra

func (m *Metadata) TypedExtra(key string) (any, bool)

TypedExtra returns the value of an extra frontmatter key with its original YAML type (bool, number, date, list, map). A string value is returned as a Go string. The second result is false when the key is absent or empty.

Emit the returned value with yaml.Marshal; a *yaml.Node keeps the author's scalar spelling, so booleans and dates are not re-quoted or reformatted.

func (*Metadata) WithoutExtra

func (m *Metadata) WithoutExtra(keys ...string) *Metadata

WithoutExtra returns a copy of the metadata without the named extra keys. The receiver is untouched: loaded metadata can be shared between loads.

type OKFConfig

type OKFConfig struct {
	// Dir is the bundle directory, relative to the project root. Default docs/okf.
	Dir string `yaml:"dir,omitempty" json:"dir,omitempty" toml:"dir,omitempty"`
	// Include limits the content kinds exported (rules, context, skills, agents,
	// commands, checks). Empty means all.
	Include []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`
	// Spec pins the OKF spec version. Only "0.2" is implemented.
	Spec string `yaml:"spec,omitempty" json:"spec,omitempty" toml:"spec,omitempty"`
	// IndexStyle selects the index.md scheme: "body" (default, the OKF 0.2
	// listing in the body) or "frontmatter" (title, version and entries in the
	// frontmatter).
	IndexStyle string `yaml:"index_style,omitempty" json:"index_style,omitempty" toml:"index_style,omitempty"`
}

OKFConfig configures the Open Knowledge Format bundle written by the okf preset and linted by `validate`.

type OutputAnalysis

type OutputAnalysis struct {
	// Path is the absolute output path, matching OutputFile.Path.
	Path string
	// Preset is the provider that rendered the output.
	Preset string
	// Scope is the scope subdirectory the output belongs to, or "" for the
	// repository root. Scoped outputs are loaded only when the agent is working
	// inside that subtree, so a report must not add them to a root total.
	Scope string
	Kind  OutputKind
	// ItemID is the skill/agent/command identifier, or the sidecar kind.
	ItemID string
	// SourcePath is the authored file this output was rendered from.
	SourcePath string
	// MachineLocal marks an output rendered from .ai-rulez/local content. It is
	// gitignored and exists only on the developer's machine.
	MachineLocal bool
	Parts        []OutputPart
}

OutputAnalysis records the classification and the per-part content of one generated output.

func (*OutputAnalysis) AddPart

func (a *OutputAnalysis) AddPart(kind PartKind, label, sourcePath, content string)

AddPart appends a measured slice. Safe to call on a nil receiver so renderers can record unconditionally without branching on whether analysis is enabled.

func (*OutputAnalysis) Enabled

func (a *OutputAnalysis) Enabled() bool

Enabled reports whether this analysis is live.

type OutputFile

type OutputFile struct {
	Path       string
	Content    string
	RawContent []byte
	Mode       os.FileMode
	IsDir      bool
	LocalOnly  bool
	// SourceHash, when set, replaces the run's Source-Hash in this output's
	// header. The agents_md shared outputs set their own (a hash of shared
	// content only); the drift guard stamps machine-local outputs with a hash of
	// their local inputs and every other output without one with the baseline hash.
	SourceHash string
	// PartiallyOwned marks a file ai-rulez only contributes some of the content
	// to — a settings document where it owns one top-level key and the consumer
	// hand-authors and tracks the rest. Such a file must not be added to the
	// managed .gitignore block, and must not be deleted as stale when it stops
	// being generated: both would destroy or hide user-authored data (#185).
	PartiallyOwned bool
	// Committed marks an output that must stay tracked in git even when
	// gitignore management is on: hosted reviewers (Bugbot, Kilo, Rovo Dev, ...)
	// only read committed files, so ignoring a check output would hide it.
	Committed bool
	// PluginInclude marks an output copied verbatim by [plugin] include into an
	// Agent Plugins package. The plugin provenance sidecar lists these paths so
	// the publish emitter can carry them without re-reading the configuration,
	// and they are not decorated with a generated-file header.
	PluginInclude bool
	// Sensitive marks an output that carries resolved MCP secrets (env values or
	// headers). It is written owner-only (0600), and an existing file is
	// tightened to that mode.
	Sensitive bool
	// OmitsRules marks a root instruction file that leaves out the rules and
	// context its tool reads from its own rules folder (junie's AGENTS.md in the
	// split rules mode). When another preset writes the same path with every rule
	// inlined, that file is the complete one and replaces this.
	OmitsRules bool
	// MergeClaims records what ai-rulez wrote into a PartiallyOwned (or merged)
	// JSON document, so it can take exactly that back out on clean or when the
	// preset or server that wrote it goes away.
	MergeClaims []jsonmerge.Claim
	// Merge, when set, lets two presets that write one document with different
	// content be combined instead of reported as a conflict: each preset's owned
	// keys are unioned and the document rendered once.
	Merge *MergeSource
}

OutputFile represents a generated output file or directory.

When RawContent is non-nil, the file is written verbatim — no header injection, no hash bookkeeping, no trailing-newline normalization. Use this for skill supporting files (references, scripts, assets) where any ai-rulez-generated marker would corrupt the payload (e.g. Python scripts, binary assets).

When RawContent is nil, Content is rendered through the standard generation pipeline (header banner + content/source hashes).

Mode is the file permission bits to apply on write (0o644 if zero). Used to preserve the executable bit on bundled skill scripts so the agent can invoke them directly. Only honored on the raw-content write path.

LocalOnly marks an output rendered from machine-local override content (.ai-rulez/local/ → CLAUDE.local.md, AGENTS.local.md, ...). Such outputs are kept out of git unconditionally — even when config gitignore is disabled — because committing them would leak machine-local configuration. A ".local." name is covered by a pattern in the managed .gitignore block; a path that exists only on this machine goes to .git/info/exclude (the .gitignore block when that is not possible).

type OutputKind

type OutputKind string

OutputKind classifies a generated output so a cost report can reason about when an agent loads it. The renderers that know the kind stamp it directly (see internal/generator/providers/render.go); anything they do not claim is classified from its path by InferOutputKind, which is a documented approximation rather than ground truth.

const (
	// OutputKindUnknown is an output no renderer claimed and whose path did not
	// match any known shape. Reports must not fold these into an always-loaded
	// total — when ai-rulez cannot say how a file is loaded it has to say so.
	OutputKindUnknown OutputKind = "unknown"
	// OutputKindRoot is a top-level instructions file (CLAUDE.md, AGENTS.md, …)
	// read at the start of every session.
	OutputKindRoot OutputKind = "root"
	// OutputKindLocalRoot is a machine-local root variant (CLAUDE.local.md).
	OutputKindLocalRoot OutputKind = "local_root"
	// OutputKindSkill is a per-skill file (.claude/skills/{id}/SKILL.md).
	OutputKindSkill OutputKind = "skill"
	// OutputKindAgent is a per-agent file (.claude/agents/{id}.md).
	OutputKindAgent OutputKind = "agent"
	// OutputKindCommand is a per-command file, which some providers render into
	// the skills directory as a user-invocable skill.
	OutputKindCommand OutputKind = "command"
	// OutputKindSidecar is a settings/manifest document (settings.json, .mcp.json).
	OutputKindSidecar OutputKind = "sidecar"
	// OutputKindResource is a bundled skill resource (references/, scripts/, assets/).
	OutputKindResource OutputKind = "resource"
	// OutputKindRuleFile is one rule or context file emitted into a provider's
	// rules directory (.cursor/rules, .devin/rules,
	// .clinerules). These providers apply such files as project instructions,
	// which makes them the rules-inline equivalent for a directory-shaped
	// provider.
	OutputKindRuleFile OutputKind = "rule_file"
	// OutputKindDirectory is an IsDir marker, which carries no content.
	OutputKindDirectory OutputKind = "directory"
)

Output kinds.

func InferOutputKind

func InferOutputKind(path, baseDir string) OutputKind

InferOutputKind classifies an output from its path. Used only for outputs the rendering layer did not classify itself — the eight hand-written preset generators in internal/generator/presets, plus scoped and machine-local variants. Returns OutputKindUnknown rather than guessing when no rule matches, so a report can say "ai-rulez does not know how this loads".

type OutputPart

type OutputPart struct {
	Kind PartKind
	// Label names the slice: a rule name, a context file name, a field name.
	Label string
	// SourcePath is the authored file the slice came from, when one exists. Used
	// to attribute a slice back to the domain that owns it.
	SourcePath string
	// Content is the rendered text, exactly as it was written into the output.
	Content string
}

OutputPart is one measured slice of a rendered output.

type OverlayChange

type OverlayChange struct {
	Path      string
	Shared    any
	Local     any
	HasShared bool
	Redacted  bool
	// Merged is the value the key has after the overlay is merged, set for keys
	// the overlay combines with the shared value instead of replacing (presets:
	// an ordered union with "!name" drops).
	Merged    any
	HasMerged bool
}

OverlayChange is one key set by the overlay with the shared value it overrides.

type PartKind

type PartKind string

PartKind labels one addressable slice of a rendered output — a section of a root instructions file, or one field of a per-item file. Splitting output this way is the whole point of the cost report: a skill's description and its body are not loaded at the same time, and reporting a single per-file number would misattribute the cost.

const (
	PartKindRootHeader       PartKind = "header"
	PartKindRootTitle        PartKind = "title"
	PartKindRootDescription  PartKind = "description"
	PartKindRootRule         PartKind = "rule"
	PartKindRootContext      PartKind = "context"
	PartKindRootAgentsRoster PartKind = "agents_delegation"
	// PartKindRootSkillsInstructions is the section telling an agent how to load
	// served skills; it is written by the renderer, not declared in a spec.
	PartKindRootSkillsInstructions PartKind = "skills_instructions"
)

Part kinds for the root instructions file, one per root.sections entry in the provider DSL.

const (
	// PartKindItemName is the item's name string, which is what a harness puts
	// in front of the model when it lists available skills.
	PartKindItemName PartKind = "name"
	// PartKindItemDescription is the frontmatter description value.
	PartKindItemDescription PartKind = "description"
	// PartKindItemFrontmatter is the rest of the frontmatter block — everything
	// except name and description, which are reported separately.
	PartKindItemFrontmatter PartKind = "frontmatter"
	// PartKindItemBody is the authored body of the item.
	PartKindItemBody PartKind = "body"
	// PartKindItemResourceIndex is the generated index of bundled resources.
	PartKindItemResourceIndex PartKind = "resource_index"
	// PartKindItemTargetedRules is the rules block appended to items that rules
	// explicitly target.
	PartKindItemTargetedRules PartKind = "targeted_rules"
	// PartKindItemTargetedContext is the context block appended to items that
	// context files explicitly target.
	PartKindItemTargetedContext PartKind = "targeted_context"
	// PartKindSidecarDocument is a whole sidecar document.
	PartKindSidecarDocument PartKind = "document"
)

Part kinds for a per-item (skill / agent / command) file.

type PathClaims

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

PathClaims records which source claimed each output path during one generation, case-insensitively because common filesystems compare paths that way. Rule files of the root and of every scope land in the same rules folders, so one PathClaims per preset is shared by all of those runs.

func NewPathClaims

func NewPathClaims() *PathClaims

NewPathClaims returns an empty PathClaims.

func (*PathClaims) Claim

func (c *PathClaims) Claim(outputPath, owner string) (previous string, ok bool)

Claim records owner as the source of outputPath. It reports the previous owner and false when a different source already claimed the path; claiming a path again for the same owner succeeds.

type Permissions

type Permissions struct {
	Allow []string `yaml:"allow,omitempty" json:"allow,omitempty" toml:"allow,omitempty"`
	Ask   []string `yaml:"ask,omitempty" json:"ask,omitempty" toml:"ask,omitempty"`
	Deny  []string `yaml:"deny,omitempty" json:"deny,omitempty" toml:"deny,omitempty"`
}

Permissions is the [permissions] block: allow, ask and deny rule lists in Claude Code permission-rule syntax ("Bash(git *)", "Read(./.env)"). It is rendered into .claude/settings.json verbatim and translated into the native permission format of the other harnesses that have one (see docs/permissions.md). Each listed rule is owned element by element, so rules the consumer wrote in the same arrays survive.

func (*Permissions) IsEmpty

func (p *Permissions) IsEmpty() bool

IsEmpty reports whether no rule is declared.

func (*Permissions) OverbroadAllowRules

func (p *Permissions) OverbroadAllowRules() []string

OverbroadAllowRules returns the allow rules that permit every call of a tool.

type PiExtras added in v5.0.4

type PiExtras struct {
	// SkillsDir is the directory each bundled skill is written to, relative to
	// the project root. Empty uses DefaultPiSkillsDir (".pi/skills"); set it to
	// "skills" to reuse the tree the other runtimes write, so a skill is written
	// once and Pi points at the same copy.
	SkillsDir string `yaml:"skills_dir,omitempty" json:"skills_dir,omitempty" toml:"skills_dir,omitempty"` //nolint:tagliatelle
	// PackageJSON is PiPackageJSONGenerate (default) to write the root
	// package.json from the plugin metadata, or PiPackageJSONMerge to update
	// only the keys ai-rulez owns and keep every other key.
	PackageJSON string `yaml:"package_json,omitempty" json:"package_json,omitempty" toml:"package_json,omitempty"` //nolint:tagliatelle
	// PackageName is the npm package name. Empty derives @<owner>/pi-<plugin-name>
	// (and, under package_json = "merge", keeps an authored name).
	PackageName string `yaml:"package_name,omitempty" json:"package_name,omitempty" toml:"package_name,omitempty"` //nolint:tagliatelle
	// SkillsEntry is PiSkillsEntryEach (default) to list every skill, or
	// PiSkillsEntryDirectory to list the skills directory once.
	SkillsEntry string `yaml:"skills_entry,omitempty" json:"skills_entry,omitempty" toml:"skills_entry,omitempty"` //nolint:tagliatelle
}

PiExtras holds Pi-package-specific packaging fields.

func (*PiExtras) MergesPackageJSON added in v5.0.4

func (e *PiExtras) MergesPackageJSON() bool

MergesPackageJSON reports whether the Pi runtime merges the root package.json instead of generating it. Safe on a nil receiver.

func (*PiExtras) ResolvedPackageJSON added in v5.0.4

func (e *PiExtras) ResolvedPackageJSON() string

ResolvedPackageJSON returns the configured package_json mode or the default. Safe on a nil receiver.

func (*PiExtras) ResolvedSkillsDir added in v5.0.4

func (e *PiExtras) ResolvedSkillsDir() string

ResolvedSkillsDir returns the configured Pi skills directory or the default. Safe on a nil receiver.

func (*PiExtras) ResolvedSkillsEntry added in v5.1.0

func (e *PiExtras) ResolvedSkillsEntry() string

ResolvedSkillsEntry returns the configured skills_entry mode or the default. Safe on a nil receiver.

func (*PiExtras) Validate added in v5.0.4

func (e *PiExtras) Validate() error

Validate checks the [plugin.pi] block. Safe on a nil receiver.

type PlacementConfig

type PlacementConfig struct {
	// Default is "core" (today's behavior) or "plugin".
	Default string `yaml:"default,omitempty" json:"default,omitempty" toml:"default,omitempty"`
	// Core and Plugin list names or globs. A pattern matches the item name or
	// "domains/<domain>/<name>". Core wins over Plugin; frontmatter `placement`
	// wins over both.
	Core   []string `yaml:"core,omitempty" json:"core,omitempty" toml:"core,omitempty"`
	Plugin []string `yaml:"plugin,omitempty" json:"plugin,omitempty" toml:"plugin,omitempty"`
	// HonorTargets makes frontmatter `targets` filter skills in .claude/skills,
	// as it already does for commands.
	HonorTargets bool `yaml:"honor_targets,omitempty" json:"honor_targets,omitempty" toml:"honor_targets,omitempty"` //nolint:tagliatelle
}

PlacementConfig is the [placement] block: whether a skill or command is generated into .claude/skills ("core") or shipped only through a plugin.

type PlannedSkill

type PlannedSkill struct {
	// ID is the skill directory name.
	ID string
	// Domain owns the skill; empty for root content.
	Domain string
	// Delivery is the effective delivery.
	Delivery Delivery
}

PlannedSkill is one skill with its effective delivery.

type PluginAuthoring

type PluginAuthoring struct {
	Name        string `yaml:"name" json:"name" toml:"name"`
	DisplayName string `yaml:"display_name,omitempty" json:"display_name,omitempty" toml:"display_name,omitempty"` //nolint:tagliatelle
	Description string `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	Version     string `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	// VersionFrom is a project-relative manifest (package.json, pyproject.toml
	// or Cargo.toml) whose declared version replaces Version. Mutually
	// exclusive with Version.
	VersionFrom string   `yaml:"version_from,omitempty" json:"version_from,omitempty" toml:"version_from,omitempty"` //nolint:tagliatelle
	Author      *Author  `yaml:"author,omitempty" json:"author,omitempty" toml:"author,omitempty"`
	Homepage    string   `yaml:"homepage,omitempty" json:"homepage,omitempty" toml:"homepage,omitempty"`
	Repository  string   `yaml:"repository,omitempty" json:"repository,omitempty" toml:"repository,omitempty"`
	License     string   `yaml:"license,omitempty" json:"license,omitempty" toml:"license,omitempty"`
	Category    string   `yaml:"category,omitempty" json:"category,omitempty" toml:"category,omitempty"`
	BrandColor  string   `yaml:"brand_color,omitempty" json:"brand_color,omitempty" toml:"brand_color,omitempty"` //nolint:tagliatelle
	Icon        string   `yaml:"icon,omitempty" json:"icon,omitempty" toml:"icon,omitempty"`
	Keywords    []string `yaml:"keywords,omitempty" json:"keywords,omitempty" toml:"keywords,omitempty"`
	Tags        []string `yaml:"tags,omitempty" json:"tags,omitempty" toml:"tags,omitempty"`
	// ContentRoot optionally points at a project-relative directory containing
	// plugin-only skills/, commands/, and agents/. Empty uses governance content.
	ContentRoot string `yaml:"content_root,omitempty" json:"content_root,omitempty" toml:"content_root,omitempty"` //nolint:tagliatelle

	// SkillsDirOwned declares each skills directory the bundle writes (the
	// parent of every generated skill directory) fully owned by the bundle:
	// verify --plugin reports an entry it did not generate there as drift of kind
	// extra, and generate --plugin removes it. Off by default, when a skill
	// directory or file that the bundle does not generate is never touched.
	SkillsDirOwned bool `yaml:"skills_dir_owned,omitempty" json:"skills_dir_owned,omitempty" toml:"skills_dir_owned,omitempty"` //nolint:tagliatelle

	// IncludeDomains lists the domains (names or globs) whose skills, commands
	// and agents are bundled next to the root content. Empty keeps the bundle
	// root-only. On a name collision the root item wins.
	IncludeDomains []string `yaml:"include_domains,omitempty" json:"include_domains,omitempty" toml:"include_domains,omitempty"` //nolint:tagliatelle

	// IncludeEvals bundles eval cases into the plugin: each skill's evals/
	// directory and the project-level .ai-rulez/evals/ tree (the latter at
	// <bundle>/evals/). Off by default, so a skill's evals/ directory is not
	// shipped to consumers of the bundle.
	IncludeEvals bool `yaml:"include_evals,omitempty" json:"include_evals,omitempty" toml:"include_evals,omitempty"` //nolint:tagliatelle

	// ProvenanceHeaders writes the "AI-RULEZ :: GENERATED FILE" header into
	// bundled Markdown/JS/TS/PY outputs. Defaults to true; set false to write
	// those outputs byte-identical to their sources. The provenance sidecar
	// still records every output's content hash either way.
	ProvenanceHeaders *bool `yaml:"provenance_headers,omitempty" json:"provenance_headers,omitempty" toml:"provenance_headers,omitempty"` //nolint:tagliatelle

	// Include lists globs (relative to the plugin's content root) of arbitrary
	// files copied verbatim into the Agent Plugins package, keeping their
	// relative path. It is how a `./`-relative MCP command can point at a
	// launcher the package ships (Agent Plugins §4.2, §7.2.1). Empty keeps the
	// package to plugin.json, mcp.json and skills/.
	Include []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`

	// SharedResources lists directories (relative to the plugin's content root)
	// whose files are copied into every bundled skill, at
	// <skill>/<directory>/... So a skill keeps the reference docs and templates
	// it links to once it is installed on its own. A relative SKILL.md link that
	// climbs to the shared directory (../../references/x.md) is rewritten to the
	// copy (references/x.md). A name clash with a file the skill already has is
	// an error. Empty copies nothing.
	SharedResources []string `yaml:"shared_resources,omitempty" json:"shared_resources,omitempty" toml:"shared_resources,omitempty"` //nolint:tagliatelle

	// Spec is the Agent Plugins version the agent-plugins, copilot and root-layout
	// codex bundles follow: "1.0.0" (the default) or "1.1.0". plugin.json and
	// mcp.json carry the matching $schema.
	Spec string `yaml:"spec,omitempty" json:"spec,omitempty" toml:"spec,omitempty"`

	// PackageManager is written as packageManager in the OpenCode and Pi root
	// package.json (for example "pnpm@9.15.0"), so a consumer can pin the
	// package manager it uses. Empty omits the field.
	PackageManager string `yaml:"package_manager,omitempty" json:"package_manager,omitempty" toml:"package_manager,omitempty"` //nolint:tagliatelle

	// OpencodeDependency overrides the "@opencode/plugin" dependency version in
	// the OpenCode root package.json. Empty uses the built-in default.
	OpencodeDependency string `yaml:"opencode_dependency,omitempty" json:"opencode_dependency,omitempty" toml:"opencode_dependency,omitempty"` //nolint:tagliatelle

	// Runtimes restricts which runtime manifests are emitted. Empty means all
	// of AllPluginRuntimes.
	Runtimes []string `yaml:"runtimes,omitempty" json:"runtimes,omitempty" toml:"runtimes,omitempty"`

	// MCP declares the plugin's bundled MCP servers using the canonical
	// ${PLUGIN_ROOT} launch variable; each runtime renderer rewrites it to the
	// runtime-specific root variable. When empty, the project's [[mcp_servers]]
	// are used as the source.
	MCP []PluginMCPLaunch `yaml:"mcp,omitempty" json:"mcp,omitempty" toml:"mcp,omitempty"`

	// Hooks declares lifecycle hooks emitted as hooks.json (Claude/Cursor) or
	// inline hooks{} (Gemini). A hook action either points at a command that
	// already exists in the consumer's environment, or declares a project-local
	// 'script' that is bundled into the plugin's hooks/ directory.
	Hooks []HookGroup `yaml:"hooks,omitempty" json:"hooks,omitempty" toml:"hooks,omitempty"`

	// Statusline is a Claude-only capability: a bundled status-line script plus
	// the slash command that wires it into user settings.
	Statusline *Statusline `yaml:"statusline,omitempty" json:"statusline,omitempty" toml:"statusline,omitempty"`

	// Interface carries the rich UI block used by Codex and Kimi manifests.
	Interface *PluginInterface `yaml:"interface,omitempty" json:"interface,omitempty" toml:"interface,omitempty"`

	// Codex selects the Codex manifest layout and marketplace index.
	Codex *CodexExtras `yaml:"codex,omitempty" json:"codex,omitempty" toml:"codex,omitempty"`

	// Cursor holds Cursor-plugin-specific packaging switches.
	Cursor *CursorExtras `yaml:"cursor,omitempty" json:"cursor,omitempty" toml:"cursor,omitempty"`

	// Gemini holds Gemini-extension-specific fields.
	Gemini *GeminiExtras `yaml:"gemini,omitempty" json:"gemini,omitempty" toml:"gemini,omitempty"`

	// Kimi holds Kimi-plugin-specific fields.
	Kimi *KimiExtras `yaml:"kimi,omitempty" json:"kimi,omitempty" toml:"kimi,omitempty"`

	// Hermes holds Hermes-Agent-specific packaging fields.
	Hermes *HermesExtras `yaml:"hermes,omitempty" json:"hermes,omitempty" toml:"hermes,omitempty"`

	// Pi holds Pi-package-specific packaging fields.
	Pi *PiExtras `yaml:"pi,omitempty" json:"pi,omitempty" toml:"pi,omitempty"`
}

PluginAuthoring describes a distributable plugin bundle authored from this project. Skills, commands, and agents are NOT declared here: they come from the existing ContentTree. This block only carries packaging metadata.

func (*PluginAuthoring) PluginContentRootDir added in v5.0.4

func (p *PluginAuthoring) PluginContentRootDir(cfg *Config, sourceDir string) string

PluginContentRootDir resolves the directory plugin shared_resources and include globs are relative to: content_root when set (under sourceDir), otherwise the config directory, otherwise sourceDir. Safe on a nil receiver.

func (*PluginAuthoring) ResolveSharedResources added in v5.0.4

func (p *PluginAuthoring) ResolveSharedResources(cfg *Config) ([]SharedResource, error)

ResolveSharedResources resolves the plugin shared_resources directories against the plugin content root. Each entry must be a safe project-relative directory that exists: a missing, non-directory or escaping entry is an error, because the bundle would otherwise silently ship skills with broken links. Safe on a nil receiver.

func (*PluginAuthoring) ResolvedRuntimes

func (p *PluginAuthoring) ResolvedRuntimes() []string

ResolvedRuntimes returns the runtimes to emit for this plugin: the explicit Runtimes list if set, otherwise AllPluginRuntimes.

func (*PluginAuthoring) ResolvedVersion added in v5.0.4

func (p *PluginAuthoring) ResolvedVersion(baseDir string) (string, error)

ResolvedVersion returns the plugin version to publish: the literal Version, or the value VersionFrom names. Safe on a nil receiver.

func (*PluginAuthoring) WritesProvenanceHeaders added in v5.0.4

func (p *PluginAuthoring) WritesProvenanceHeaders() bool

WritesProvenanceHeaders reports whether bundled comment-safe outputs carry the AI-RULEZ generated-file header. It defaults to true, so only an explicit false turns the headers off. Safe on a nil receiver.

type PluginConfig

type PluginConfig struct {
	Marketplace string `yaml:"marketplace" json:"marketplace" toml:"marketplace"`
	Name        string `yaml:"name" json:"name" toml:"name"`
	Scope       string `yaml:"scope,omitempty" json:"scope,omitempty" toml:"scope,omitempty"` // "project" or "user"
	Enabled     *bool  `yaml:"enabled,omitempty" json:"enabled,omitempty" toml:"enabled,omitempty"`
}

PluginConfig represents a plugin to install from a marketplace

func (*PluginConfig) GetScope

func (p *PluginConfig) GetScope() string

GetScope returns the scope, defaulting to "project"

func (*PluginConfig) IsEnabled

func (p *PluginConfig) IsEnabled() bool

IsEnabled returns true if the plugin is enabled (defaults to true)

type PluginDefaults

type PluginDefaults struct {
	Version  string   `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	Category string   `yaml:"category,omitempty" json:"category,omitempty" toml:"category,omitempty"`
	Keywords []string `yaml:"keywords,omitempty" json:"keywords,omitempty" toml:"keywords,omitempty"`
	// DefaultEnabled is emitted as the marketplace entry's defaultEnabled
	// (Claude Code starts the plugin disabled when false). Unset omits it.
	DefaultEnabled *bool    `yaml:"default_enabled,omitempty" json:"default_enabled,omitempty" toml:"default_enabled,omitempty"` //nolint:tagliatelle
	Runtimes       []string `yaml:"runtimes,omitempty" json:"runtimes,omitempty" toml:"runtimes,omitempty"`
}

PluginDefaults are the per-plugin fields shared by from_domains and [[marketplace.plugins]]. Unset fields fall back to the plugin block.

type PluginInterface

type PluginInterface struct {
	DisplayName       string   `yaml:"display_name,omitempty" json:"display_name,omitempty" toml:"display_name,omitempty"`                //nolint:tagliatelle
	ShortDescription  string   `yaml:"short_description,omitempty" json:"short_description,omitempty" toml:"short_description,omitempty"` //nolint:tagliatelle
	LongDescription   string   `yaml:"long_description,omitempty" json:"long_description,omitempty" toml:"long_description,omitempty"`    //nolint:tagliatelle
	DeveloperName     string   `yaml:"developer_name,omitempty" json:"developer_name,omitempty" toml:"developer_name,omitempty"`          //nolint:tagliatelle
	Category          string   `yaml:"category,omitempty" json:"category,omitempty" toml:"category,omitempty"`
	Capabilities      []string `yaml:"capabilities,omitempty" json:"capabilities,omitempty" toml:"capabilities,omitempty"`
	DefaultPrompt     []string `yaml:"default_prompt,omitempty" json:"default_prompt,omitempty" toml:"default_prompt,omitempty"`                   //nolint:tagliatelle
	WebsiteURL        string   `yaml:"website_url,omitempty" json:"website_url,omitempty" toml:"website_url,omitempty"`                            //nolint:tagliatelle
	PrivacyPolicyURL  string   `yaml:"privacy_policy_url,omitempty" json:"privacy_policy_url,omitempty" toml:"privacy_policy_url,omitempty"`       //nolint:tagliatelle
	TermsOfServiceURL string   `yaml:"terms_of_service_url,omitempty" json:"terms_of_service_url,omitempty" toml:"terms_of_service_url,omitempty"` //nolint:tagliatelle
	BrandColor        string   `yaml:"brand_color,omitempty" json:"brand_color,omitempty" toml:"brand_color,omitempty"`                            //nolint:tagliatelle
	ComposerIcon      string   `yaml:"composer_icon,omitempty" json:"composer_icon,omitempty" toml:"composer_icon,omitempty"`                      //nolint:tagliatelle
	LogoDark          string   `yaml:"logo_dark,omitempty" json:"logo_dark,omitempty" toml:"logo_dark,omitempty"` //nolint:tagliatelle
	Screenshots       []string `yaml:"screenshots,omitempty" json:"screenshots,omitempty" toml:"screenshots,omitempty"`
}

PluginInterface is the rich UI block shared by Codex and Kimi manifests.

type PluginMCPLaunch

type PluginMCPLaunch struct {
	Name      string            `yaml:"name" json:"name" toml:"name"`
	Command   string            `yaml:"command,omitempty" json:"command,omitempty" toml:"command,omitempty"`
	Args      []string          `yaml:"args,omitempty" json:"args,omitempty" toml:"args,omitempty"`
	Env       map[string]string `yaml:"env,omitempty" json:"env,omitempty" toml:"env,omitempty"`
	Transport string            `yaml:"transport,omitempty" json:"transport,omitempty" toml:"transport,omitempty"`
	URL       string            `yaml:"url,omitempty" json:"url,omitempty" toml:"url,omitempty"`

	// Disabled carries a project server's enabled = false into the bundle. It is
	// not authored: [[plugin.mcp]] entries are always enabled.
	Disabled bool `yaml:"-" json:"-" toml:"-"`
}

PluginMCPLaunch is a bundled MCP server launch declaration for a plugin. For stdio servers the command may reference ${PLUGIN_ROOT}; renderers rewrite it per runtime. For remote servers, set Transport ("http"/"sse") and URL instead.

type PluginRelevance

type PluginRelevance struct {
	Topic   string           `yaml:"topic,omitempty" json:"topic,omitempty" toml:"topic,omitempty"`
	Signals RelevanceSignals `yaml:"signals" json:"signals" toml:"signals"`
}

PluginRelevance is the marketplace entry's relevance object: when Claude Code suggests installing the plugin. See https://code.claude.com/docs/en/plugins/relevance.

type PluginResolution added in v5.5.0

type PluginResolution struct {
	Skills   []ContentFile
	Agents   []ContentFile
	Commands []ContentFile
	Rules    []ContentFile
	Hooks    []HookGroup
	MCP      []MCPServer
	Scan     []ImportedScanFile
}

PluginResolution is what resolving the installed plugins of a configuration delivered. Content files carry their entry's scope (Profiles, Domain, Trust).

type PluginSelectorProblem added in v5.2.0

type PluginSelectorProblem struct {
	Plugin  string
	Field   string
	Pattern string
	Message string
}

PluginSelectorProblem is a selector of a plugin definition that matches no content of the project.

type PolicyEnforcer

type PolicyEnforcer interface {
	// Enforce clamps cfg to the policy and reports what it had to clamp. A nil
	// outcome with a nil error means no policy applies. An error means the
	// policy was demanded but could not be used (fail closed).
	Enforce(ctx context.Context, cfg *Config) (*PolicyOutcome, error)
	// Locks reports whether the policy forbids the named network feature
	// ("telemetry", "llm") regardless of user scope. It must fail closed.
	Locks(feature string) bool
}

PolicyEnforcer applies an organization policy to a freshly loaded configuration. The cmd package installs one; library users that never do get no policy and no behavior change.

type PolicyOutcome

type PolicyOutcome struct {
	// Violations lists every attempt to loosen the policy, in a stable order.
	Violations []PolicyViolation
	// SeverityFloor maps a rule code to the lowest severity the policy allows;
	// the lint package raises the effective severity to it.
	SeverityFloor map[string]string
	// RequiredCodes lists the rule codes that may not be turned off or ignored.
	RequiredCodes []string
	// MaxFindings maps a rule code to the most findings of it the policy
	// allows; the lint package reports AR749 for a code over its ceiling.
	MaxFindings map[string]int
	// NoInlineIgnore lists the rule codes whose inline `ai-rulez-lint-ignore`
	// comments are not honored.
	NoInlineIgnore []string
	// Accepted lists repository overrides the policy allowed, such as an
	// allowlist the repository narrowed.
	Accepted []string
	// MCPReadOnly is true when the policy requires `ai-rulez mcp` to be
	// read-only (mcp.read_only = true). The command line cannot loosen it.
	MCPReadOnly bool
	// Warn is --policy-mode warn: the values are still clamped, but the
	// violations are reported as warnings and do not fail the run.
	Warn bool
}

PolicyOutcome is the result of applying the organization policy to a loaded configuration. It is nil when no policy is in force.

type PolicySubject

type PolicySubject struct {
	Kind   string `json:"kind"`
	Name   string `json:"name"`
	Domain string `json:"domain,omitempty"`
	Digest string `json:"digest"`
}

PolicySubject is one pinned entry of the lock: its lock kind ("include", "skill", "source", "served", or an authored item kind), its name or id, the domain or serve view that qualifies it, and the digest the lock pins.

type PolicyViolation

type PolicyViolation struct {
	// Code is the rule code (AR740 ... AR745).
	Code string `json:"code"`
	// Key is the dotted configuration key, for example "lint.severity.AR008".
	Key string `json:"key"`
	// File is the configuration file that set the key.
	File string `json:"file,omitempty"`
	// Line is the best-known line of the key in File, 1 when unknown.
	Line int `json:"line,omitempty"`
	// Message states the attempt and the bound it broke.
	Message string `json:"message"`
	// Origin names the policy layer that sets the bound ("managed", "flag", "env").
	Origin string `json:"origin,omitempty"`
	// Subject names the pinned content a violation is about, when it is about
	// one (sources.deny_digests, AR747); nil otherwise. Consumers match on it,
	// never on Message.
	Subject *PolicySubject `json:"subject,omitempty"`
}

PolicyViolation is one place where the repository configuration tried to loosen an organization policy (docs/policy.md). The policy package fills it; the lint package reports it as AR740-AR745.

type Preset

type Preset struct {
	// Built-in preset (e.g., "claude", "cursor")
	BuiltIn string `yaml:"-" json:"-" toml:"-"`

	// Custom preset fields
	Name     string     `yaml:"name,omitempty" json:"name,omitempty" toml:"name,omitempty"`
	Type     PresetType `yaml:"type,omitempty" json:"type,omitempty" toml:"type,omitempty"`
	Path     string     `yaml:"path,omitempty" json:"path,omitempty" toml:"path,omitempty"`
	Template string     `yaml:"template,omitempty" json:"template,omitempty" toml:"template,omitempty"`

	// Provider references a declarative provider spec (schema/provider.schema.json)
	// relative to the project root. A provider-backed preset has full parity with
	// a built-in preset (root file, skills/agents/commands, frontmatter, MCP
	// sidecars). When set, Type and Path must be empty.
	Provider string `yaml:"provider,omitempty" json:"provider,omitempty" toml:"provider,omitempty"`
}

Preset represents either a built-in preset name or a custom preset configuration

func (*Preset) GetName

func (p *Preset) GetName() string

GetName returns the preset name (built-in or custom)

func (*Preset) IsBuiltIn

func (p *Preset) IsBuiltIn() bool

IsBuiltIn returns true if this is a built-in preset

func (*Preset) IsValid

func (p *Preset) IsValid() bool

IsValid returns true if the preset is valid

For a built-in name the answer depends on registration: provider-spec presets are known only once internal/generator/providers is linked into the binary (see AllPresetNames). Import internal/generator before validating presets.

func (Preset) MarshalJSON

func (p Preset) MarshalJSON() ([]byte, error)

MarshalJSON implements custom JSON marshaling for Preset

func (*Preset) UnmarshalJSON

func (p *Preset) UnmarshalJSON(data []byte) error

UnmarshalJSON implements custom JSON unmarshaling for Preset

type PresetGenerator

type PresetGenerator interface {
	Generate(content *ContentTree, baseDir string, config *Config) ([]OutputFile, error)
	GetOutputPaths(baseDir string) []string
	GetName() string
}

PresetGenerator defines the interface for preset generators

type PresetName

type PresetName string
const (
	PresetClaude      PresetName = "claude"
	PresetCursor      PresetName = "cursor"
	PresetDevin       PresetName = "devin"
	PresetCopilot     PresetName = "copilot"
	PresetGemini      PresetName = "gemini"
	PresetCline       PresetName = "cline"
	PresetAmp         PresetName = "amp"
	PresetCodex       PresetName = "codex"
	PresetJunie       PresetName = "junie"
	PresetHermes      PresetName = "hermes"
	PresetOpenCode    PresetName = "opencode"
	PresetAntigravity PresetName = "antigravity"
	PresetMCP         PresetName = "mcp"
	PresetXum         PresetName = "xum"
	PresetPi          PresetName = "pi"
	PresetBaz         PresetName = "baz"
)

type PresetType

type PresetType string

PresetType defines the type of custom preset output

const (
	PresetTypeMarkdown  PresetType = "markdown"
	PresetTypeDirectory PresetType = "directory"
	PresetTypeJSON      PresetType = "json"
)

type Priority

type Priority string
const (
	PriorityCritical Priority = "critical"
	PriorityHigh     Priority = "high"
	PriorityMedium   Priority = "medium"
	PriorityLow      Priority = "low"
	PriorityMinimal  Priority = "minimal"
)

func IntToPriority

func IntToPriority(i int) Priority

func ParsePriority

func ParsePriority(value interface{}) (Priority, error)

func (Priority) IsValid

func (p Priority) IsValid() bool

func (Priority) String

func (p Priority) String() string

func (Priority) ToInt

func (p Priority) ToInt() int

type PublishConfig

type PublishConfig struct {
	// Runtimes restricts the published bundle to these plugin runtimes
	// (default: the [plugin] runtimes). `publish --runtime` overrides it.
	Runtimes []string `yaml:"runtimes,omitempty" json:"runtimes,omitempty" toml:"runtimes,omitempty"`
	// RequireSignature makes publish fail unless it signs the archive
	// (`--sign-key` or `--sign-keyless`); `publish emit` is not gated.
	RequireSignature bool `yaml:"require_signature,omitempty" json:"require_signature,omitempty" toml:"require_signature,omitempty"` //nolint:tagliatelle
	// RequireApproved makes publish fail unless every content item the
	// [governance] policy selects is approved in ai-rulez.lock.
	RequireApproved bool `yaml:"require_approved,omitempty" json:"require_approved,omitempty" toml:"require_approved,omitempty"` //nolint:tagliatelle
	// AllowDirty waives the clean-tree gate (the manifest records dirty = true).
	AllowDirty bool `yaml:"allow_dirty,omitempty" json:"allow_dirty,omitempty" toml:"allow_dirty,omitempty"` //nolint:tagliatelle

	GitHubRelease *PublishGitHubRelease `yaml:"github_release,omitempty" json:"github_release,omitempty" toml:"github_release,omitempty"` //nolint:tagliatelle
	OCI           *PublishOCI           `yaml:"oci,omitempty" json:"oci,omitempty" toml:"oci,omitempty"`
	NPM           *PublishNPM           `yaml:"npm,omitempty" json:"npm,omitempty" toml:"npm,omitempty"`
	Marketplace   *PublishMarketplace   `yaml:"marketplace,omitempty" json:"marketplace,omitempty" toml:"marketplace,omitempty"`
	Emitters      []PublishEmitter      `yaml:"emitters,omitempty" json:"emitters,omitempty" toml:"emitters,omitempty"`
}

PublishConfig is the [publish] table (docs/publish.md). It holds no credential: registries and forges authenticate through their own CLIs and environment, never through the repository.

type PublishEmitter

type PublishEmitter struct {
	Name string `yaml:"name" json:"name" toml:"name"`
	// Template is the project-relative text/template file of the "template" emitter.
	Template string `yaml:"template,omitempty" json:"template,omitempty" toml:"template,omitempty"`
	// Output renames the template's output file under <dist>/emit/.
	Output string `yaml:"output,omitempty" json:"output,omitempty" toml:"output,omitempty"`
	// Options are the emitter's own settings, such as blueprint for "port".
	Options map[string]string `yaml:"options,omitempty" json:"options,omitempty" toml:"options,omitempty"`
}

PublishEmitter is one [[publish.emitters]] entry.

type PublishGitHubRelease

type PublishGitHubRelease struct {
	// Repo is OWNER/REPO (or HOST/OWNER/REPO); default [plugin] repository, else origin.
	Repo string `yaml:"repo,omitempty" json:"repo,omitempty" toml:"repo,omitempty"`
}

PublishGitHubRelease is [publish.github_release].

type PublishMarketplace

type PublishMarketplace struct {
	// Channels maps a channel name to the git ref its pinned index points at.
	// A channel not listed here pins the release tag.
	Channels map[string]string `yaml:"channels,omitempty" json:"channels,omitempty" toml:"channels,omitempty"`
}

PublishMarketplace is [publish.marketplace].

type PublishNPM

type PublishNPM struct {
	// Scope is the npm scope the package is published under, "@acme".
	Scope string `yaml:"scope,omitempty" json:"scope,omitempty" toml:"scope,omitempty"`
	// Access is "restricted" (default) or "public".
	Access string `yaml:"access,omitempty" json:"access,omitempty" toml:"access,omitempty"`
	// Registry is the registry URL (https only); empty uses the npm client's.
	Registry string `yaml:"registry,omitempty" json:"registry,omitempty" toml:"registry,omitempty"`
}

PublishNPM is [publish.npm].

type PublishOCI

type PublishOCI struct {
	// Ref is the repository the artifact is pushed to (host/path, no tag or
	// digest); the tag is the plugin version.
	Ref string `yaml:"ref,omitempty" json:"ref,omitempty" toml:"ref,omitempty"`
}

PublishOCI is [publish.oci].

type Registry

type Registry struct {

	// Custom builds the generator of a custom preset declared in the config.
	Custom func(Preset) PresetGenerator
	// Provider builds the generator of a provider-backed custom preset from its spec
	// file, resolved against baseDir (the project root) and read through view, so a
	// project held in memory or in a snapshot, and the symlink policy of the
	// workspace, apply to the spec as to any other source.
	Provider func(preset Preset, baseDir string, view workspace.View) (PresetGenerator, error)
	// contains filtered or unexported fields
}

Registry maps preset names to their generators and holds the factories custom presets are built with. config cannot import the packages that implement the presets (they import config), so the engine builds a Registry above both (internal/generator/registry) and hands it to the load (WithRegistry) or to the Generator. A Registry is filled while it is constructed and only read afterwards, which makes one value safe to share between runs on different projects; nothing is registered from an init().

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty Registry.

func (*Registry) Each

func (r *Registry) Each(fn func(name string, generator PresetGenerator))

Each calls fn for every registered generator, in name order.

func (*Registry) Generator

func (r *Registry) Generator(name string) (PresetGenerator, error)

Generator returns the built-in preset generator with the given name, or ErrInvalidPreset.

func (*Registry) Names

func (r *Registry) Names() []string

Names returns the registered preset names, sorted.

func (*Registry) Register

func (r *Registry) Register(name string, generator PresetGenerator)

Register adds a built-in preset generator. It is for the code that builds the Registry, before anything reads it.

type RelevanceSignals

type RelevanceSignals struct {
	CWD          []string           `yaml:"cwd,omitempty" json:"cwd,omitempty" toml:"cwd,omitempty"`
	CLI          []string           `yaml:"cli,omitempty" json:"cli,omitempty" toml:"cli,omitempty"`
	Hosts        []string           `yaml:"hosts,omitempty" json:"hosts,omitempty" toml:"hosts,omitempty"`
	FilesRead    []string           `yaml:"files_read,omitempty" json:"files_read,omitempty" toml:"files_read,omitempty"`          //nolint:tagliatelle
	ManifestDeps []ManifestDepMatch `yaml:"manifest_deps,omitempty" json:"manifest_deps,omitempty" toml:"manifest_deps,omitempty"` //nolint:tagliatelle
}

RelevanceSignals are the matchers of a relevance object. At least one is set.

func (RelevanceSignals) IsEmpty

func (s RelevanceSignals) IsEmpty() bool

IsEmpty reports whether no signal is set.

type ResolveIncludesCallback

type ResolveIncludesCallback func(ctx context.Context, cfg *Config) (*ContentTree, error)

ResolveIncludesCallback resolves the includes of a loaded configuration into the content tree to use. The includes package supplies it (config cannot import it).

type ResolveInstalledPluginsCallback added in v5.5.0

type ResolveInstalledPluginsCallback func(ctx context.Context, cfg *Config) (*PluginResolution, error)

ResolveInstalledPluginsCallback resolves the installed marketplaces and plugins of a loaded configuration.

type ResolveInstalledSkillsCallback

type ResolveInstalledSkillsCallback func(ctx context.Context, cfg *Config) ([]ContentFile, error)

ResolveInstalledSkillsCallback resolves the installed skills of a loaded configuration.

type ResolvedRole

type ResolvedRole struct {
	Name        string     `json:"name"`
	Description string     `json:"description,omitempty"`
	Extends     string     `json:"extends,omitempty"`
	Match       *RoleMatch `json:"match,omitempty"`
	// Domains are the domain ids the role selects (own and inherited), in declaration order.
	Domains []string `json:"domains"`
	// Items are the kept items, sorted by kind, then domain, then id.
	Items []RoleItem `json:"items"`
	// Context lists the context files that stay, as they are not role-selectable.
	SkillOverrides map[string]string `json:"skill_overrides,omitempty"`
	// DeliveryOverride is the per-skill delivery the role sets (see RoleDeliveryOverride).
	DeliveryOverride map[string]string `json:"-"`
	// contains filtered or unexported fields
}

ResolvedRole is the effective item set of a role.

func (*ResolvedRole) Flat

func (r *ResolvedRole) Flat() *RoleConfig

Flat returns the merged role configuration.

func (*ResolvedRole) ItemsOf

func (r *ResolvedRole) ItemsOf(kind string) []RoleItem

ItemsOf returns the kept items of one kind.

func (*ResolvedRole) SkillOverrideMap

func (r *ResolvedRole) SkillOverrideMap() map[string]string

SkillOverrideMap returns skillOverrides.<skill> entries for the explicit skill_mode values of kept skills, keyed by skill id.

type Resolvers

type Resolvers struct {
	Includes ResolveIncludesCallback
	Skills   ResolveInstalledSkillsCallback
	// Plugins resolves [[installed_marketplaces]] and [[installed_plugins]].
	Plugins ResolveInstalledPluginsCallback
	// Agents completes the content tree of the "agents" source (AGENTS.md and
	// generated-file screening); only a project with that source needs it.
	Agents AgentsSourceReader
}

Resolvers fetch what a configuration pulls in from outside the project: includes and installed skills. They are passed to the load (WithResolvers) rather than registered process-wide, so a caller that fetches nothing (an embedding service, a test) simply passes none, and one process can serve callers with different credentials. A configuration without includes or installed skills never needs them.

type ReviewConfig

type ReviewConfig struct {
	// Rubric is the rubric id: `builtin:skill-quality` or a directory under .ai-rulez/rubrics.
	Rubric string `yaml:"rubric,omitempty" json:"rubric,omitempty" toml:"rubric,omitempty"`
	// Content is descriptions (name, description and frontmatter keys; the default) or full (adds the body).
	Content string `yaml:"content,omitempty" json:"content,omitempty" toml:"content,omitempty"`
	// Exclude lists name globs of items the review skips.
	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty" toml:"exclude,omitempty"`
	// MaxCostUSD is the spend ceiling of one run (0 = unset). A repository value can only
	// lower the default; the user config file and --max-cost can set any value.
	MaxCostUSD float64 `yaml:"max_cost_usd,omitempty" json:"max_cost_usd,omitempty" toml:"max_cost_usd,omitempty"` //nolint:tagliatelle
	// MaxCalls is the call ceiling of one run (0 = unset); the same trust rule as MaxCostUSD.
	MaxCalls int `yaml:"max_calls,omitempty" json:"max_calls,omitempty" toml:"max_calls,omitempty"` //nolint:tagliatelle
	// OnSecret is withhold (default: an item holding a credential is never sent) or redact (the
	// credential is masked and the item is sent, marked redacted).
	OnSecret string `yaml:"on_secret,omitempty" json:"on_secret,omitempty" toml:"on_secret,omitempty"` //nolint:tagliatelle
	// AllowedHosts, when set in user scope, is the only list of hosts a judged run may send to.
	// A repository config cannot set it: it is ignored and reported.
	AllowedHosts []string `yaml:"allowed_hosts,omitempty" json:"allowed_hosts,omitempty" toml:"allowed_hosts,omitempty"` //nolint:tagliatelle
	// Gate is the [review.gate] table.
	Gate *ReviewGateConfig `yaml:"gate,omitempty" json:"gate,omitempty" toml:"gate,omitempty"`
	// Fix is the [review.fix] table.
	Fix *ReviewFixConfig `yaml:"fix,omitempty" json:"fix,omitempty" toml:"fix,omitempty"`
}

ReviewConfig is the [review] table: which rubric scores the content, what an item sends to a judge, the spend ceilings of a judged run, and the gate and fix settings (docs/review.md). allowed_hosts is honored from user scope only.

func (*ReviewConfig) FixMaxGrowthPercent

func (r *ReviewConfig) FixMaxGrowthPercent() int

FixMaxGrowthPercent returns the effective growth limit of a fix.

func (*ReviewConfig) GateLevel

func (r *ReviewConfig) GateLevel() string

GateLevel returns the effective gate level.

func (*ReviewConfig) GateRequiresCalibration

func (r *ReviewConfig) GateRequiresCalibration() bool

GateRequiresCalibration reports whether --gate needs a matching calibration record.

func (*ReviewConfig) OnSecretMode

func (r *ReviewConfig) OnSecretMode() string

OnSecretMode returns the effective on_secret value.

func (*ReviewConfig) Validate

func (r *ReviewConfig) Validate() []string

Validate returns the problems of the table, one message each.

type ReviewFixConfig

type ReviewFixConfig struct {
	// Model writes the fix. It must differ from the judge model unless --allow-same-model is given.
	Model string `yaml:"model,omitempty" json:"model,omitempty" toml:"model,omitempty"`
	// MaxGrowthPercent bounds how much a fix may grow an item (default 25).
	MaxGrowthPercent int `yaml:"max_growth_percent,omitempty" json:"max_growth_percent,omitempty" toml:"max_growth_percent,omitempty"` //nolint:tagliatelle
}

ReviewFixConfig is the [review.fix] table.

type ReviewGateConfig

type ReviewGateConfig struct {
	// Level is the lowest severity ceiling of a dimension whose stable fail verdict fails the gate.
	Level string `yaml:"level,omitempty" json:"level,omitempty" toml:"level,omitempty"`
	// RequireCalibration, when false, lets --gate run without a matching calibration record
	// (AR9G9 still reports it). Nil means true.
	RequireCalibration *bool `yaml:"require_calibration,omitempty" json:"require_calibration,omitempty" toml:"require_calibration,omitempty"` //nolint:tagliatelle
	// CalibrationMaxAgeDays overrides the rubric's calibration.max_age_days.
	CalibrationMaxAgeDays int `yaml:"calibration_max_age_days,omitempty" json:"calibration_max_age_days,omitempty" toml:"calibration_max_age_days,omitempty"` //nolint:tagliatelle
}

ReviewGateConfig is the [review.gate] table: when --gate may fail a build.

type ReviewResolution

type ReviewResolution struct {
	// AllowedHosts is the user-scope [review] allowed_hosts (or AI_RULEZ_REVIEW_ALLOWED_HOSTS).
	AllowedHosts []string
	// MaxCostUSD and MaxCalls are the user-scope ceilings (0 = unset).
	MaxCostUSD float64
	MaxCalls   int
	// IgnoredRepoKeys names the user-scope-only keys a repository config set; they have no effect.
	IgnoredRepoKeys []string
	// UserFile is the user config path that was read ("" when none exists).
	UserFile string
}

ReviewResolution is what only user scope may say about `ai-rulez review`: the hosts a judged run may send to, and spend ceilings the repository cannot lower or raise for you.

func (ReviewResolution) Caps

func (r ReviewResolution) Caps(repo *ReviewConfig, defCost float64, defCalls int) (cost float64, calls int)

Caps resolves the spend ceilings of a judged run. A flag wins. Otherwise a user-scope value wins, and a repository value can only lower the default: a hostile repository must not be able to raise what a run may spend on your credentials.

func (ReviewResolution) HostAllowed

func (r ReviewResolution) HostAllowed(host string) bool

HostAllowed reports whether a judged run may send to host (host or host:port as written in base_url; "" means the provider's own endpoint). An empty allow-list allows any host.

type RoleConfig

type RoleConfig struct {
	Name        string        `yaml:"name" json:"name" toml:"name"`
	Description string        `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	Extends     string        `yaml:"extends,omitempty" json:"extends,omitempty" toml:"extends,omitempty"`
	Domains     []string      `yaml:"domains,omitempty" json:"domains,omitempty" toml:"domains,omitempty"`
	Skills      *RoleSelector `yaml:"skills,omitempty" json:"skills,omitempty" toml:"skills,omitempty"`
	Rules       *RoleSelector `yaml:"rules,omitempty" json:"rules,omitempty" toml:"rules,omitempty"`
	Agents      *RoleSelector `yaml:"agents,omitempty" json:"agents,omitempty" toml:"agents,omitempty"`
	Commands    *RoleSelector `yaml:"commands,omitempty" json:"commands,omitempty" toml:"commands,omitempty"`
	Checks      *RoleSelector `yaml:"checks,omitempty" json:"checks,omitempty" toml:"checks,omitempty"`
	// SkillMode maps a skill id or glob to on, name-only, user-invocable-only or
	// off, Claude Code's skillOverrides values.
	SkillMode map[string]string `yaml:"skill_mode,omitempty" json:"skill_mode,omitempty" toml:"skill_mode,omitempty"` //nolint:tagliatelle
	// Delivery maps a skill id or glob ("domain/id" for one domain) to static,
	// served or both: how a skill reaches this role's agent. It sits between a
	// skill's own `delivery` frontmatter and the domain and global defaults (see
	// EffectiveDelivery) and is inherited through extends like SkillMode.
	Delivery map[string]string `yaml:"delivery,omitempty" json:"delivery,omitempty" toml:"delivery,omitempty"`
	Match    *RoleMatch        `yaml:"match,omitempty" json:"match,omitempty" toml:"match,omitempty"`
	// Pin records the digest of this role's rendered outputs in ai-rulez.lock
	// (lock --roles pins every role). It is not inherited through extends.
	Pin bool `yaml:"pin,omitempty" json:"pin,omitempty" toml:"pin,omitempty"`
}

RoleConfig is one [[roles]] entry.

func (*RoleConfig) DeliveryFor

func (r *RoleConfig) DeliveryFor(domain, id string) (Delivery, bool)

DeliveryFor returns the delivery the role sets for a skill, with the same specificity rule as SkillModeFor. The bool is false when it sets none.

func (*RoleConfig) Keeps

func (r *RoleConfig) Keeps(kind, domain, id string) bool

Keeps reports whether a flat role keeps the item of the given kind. domain is empty for root content.

func (*RoleConfig) SkillModeFor

func (r *RoleConfig) SkillModeFor(domain, id string) (string, bool)

SkillModeFor returns the explicit skill_mode of a skill: an exact id wins over a glob, a longer glob over a shorter one, and ties go to the lexically first pattern, so the answer never depends on map order. The bool is false when the role does not mention the skill.

type RoleItem

type RoleItem struct {
	Kind   string `json:"kind"`
	ID     string `json:"id"`
	Domain string `json:"domain,omitempty"`
	// Mode is the skill_mode of a skill the role sets explicitly.
	Mode string `json:"mode,omitempty"`
	// Delivery is how a kept skill reaches the role's agent (static, served or
	// both), after the role's delivery, the skill's own and the defaults.
	Delivery string `json:"delivery,omitempty"`
	// Path is the source file, relative to the configuration directory when it lies below it.
	Path string `json:"path,omitempty"`

	File *ContentFile `json:"-"`
}

RoleItem is one item a role keeps.

type RoleManifestConfig

type RoleManifestConfig struct {
	// Enabled writes <config dir>/roles.json on generate.
	Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty" toml:"enabled,omitempty"`
	// SkillModeFallback is what generate --role does with a skill whose skill_mode is
	// "off" on a harness that has no documented setting for it: "drop" (default)
	// leaves the skill out, "serve" moves it to served delivery. It lives here
	// because TOML cannot use roles as both an array and a table.
	SkillModeFallback string `yaml:"skill_mode_fallback,omitempty" json:"skill_mode_fallback,omitempty" toml:"skill_mode_fallback,omitempty"` //nolint:tagliatelle
}

RoleManifestConfig is the [role_manifest] table. The role list itself is the [[roles]] array, so the manifest switch cannot live in a [roles] table.

type RoleMatch

type RoleMatch struct {
	Groups []string `yaml:"groups,omitempty" json:"groups,omitempty" toml:"groups,omitempty"`
}

RoleMatch holds hints for an external tool that maps people to roles. ai-rulez stores and republishes them and never interprets them.

type RoleProblem

type RoleProblem struct {
	// Kind is one of the RoleProblem* constants.
	Kind string `json:"kind"`
	Role string `json:"role"`
	// Message is a complete sentence naming the role.
	Message string `json:"message"`
}

RoleProblem is a role defect found against the loaded configuration.

type RoleSelector

type RoleSelector struct {
	Include []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`
	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty" toml:"exclude,omitempty"`
}

RoleSelector include/exclude lists for one item kind. Entries are item ids (the rule, skill, agent or command name) or path.Match globs over the id. An entry containing a slash is matched against "<domain>/<id>". An empty Include means every item the role's domains provide.

type RulesConfig

type RulesConfig struct {
	// Mode is the default rules output mode: "split" or "inline".
	Mode string `yaml:"mode,omitempty" json:"mode,omitempty" toml:"mode,omitempty"`

	// ModeByPreset overrides Mode for specific presets. Keys are preset names.
	ModeByPreset map[string]string `yaml:"mode_by_preset,omitempty" json:"mode_by_preset,omitempty" toml:"mode_by_preset,omitempty"` //nolint:tagliatelle

	// BazScoped says where the baz preset puts path-scoped rules and context:
	// "nested" (the default) writes them to the AGENTS.md of the directory the
	// globs point into, "root" keeps them in the root AGENTS.md with an
	// "Applies to" line.
	BazScoped string `yaml:"baz_scoped,omitempty" json:"baz_scoped,omitempty" toml:"baz_scoped,omitempty"` //nolint:tagliatelle

	// BazDefault selects which items reach the baz preset's path-scoped
	// AGENTS.md files: "all" (the default) lets every item through, "none"
	// only items whose frontmatter `targets` name baz.
	BazDefault string `yaml:"baz_default,omitempty" json:"baz_default,omitempty" toml:"baz_default,omitempty"` //nolint:tagliatelle

	// BazUnscoped says what the baz preset does with a glob-activated item that
	// cannot be nested because no glob names a directory (`**/*.go`, `**/BUILD`):
	// "root" (the default) inlines it into the root AGENTS.md, "skip" leaves it
	// out of that file when baz is its only writer.
	BazUnscoped string `yaml:"baz_unscoped,omitempty" json:"baz_unscoped,omitempty" toml:"baz_unscoped,omitempty"` //nolint:tagliatelle

	// BazRest says what the baz preset does with a glob-activated item it does not
	// nest (not selected under baz_default = "none", or excluded by `!baz`):
	// "root" (the default) leaves it inlined in the root AGENTS.md, "skip" leaves
	// it out of that file when baz is its only writer.
	BazRest string `yaml:"baz_rest,omitempty" json:"baz_rest,omitempty" toml:"baz_rest,omitempty"` //nolint:tagliatelle

	// BazDedupe, when true, writes a path-scoped item once into a shared parent
	// AGENTS.md when its globs point into several directories under one parent,
	// instead of a copy in each. Off by default so existing output is unchanged.
	BazDedupe bool `yaml:"baz_dedupe,omitempty" json:"baz_dedupe,omitempty" toml:"baz_dedupe,omitempty"` //nolint:tagliatelle

	// BazDedupeMax, when above 0, collapses an item that would be copied into
	// more than that many directories to the directory covering all of them, up
	// to the repository root (the root AGENTS.md). 0 (the default) is off.
	BazDedupeMax int `yaml:"baz_dedupe_max,omitempty" json:"baz_dedupe_max,omitempty" toml:"baz_dedupe_max,omitempty"` //nolint:tagliatelle

	// BazBudget is the byte budget of the AGENTS.md files the baz preset
	// writes; generate warns when their total exceeds it. 0 (the default)
	// disables the warning.
	BazBudget int `yaml:"baz_budget,omitempty" json:"baz_budget,omitempty" toml:"baz_budget,omitempty"` //nolint:tagliatelle

	// Baz holds per-rule controls for the baz preset ([rules.baz]).
	Baz *BazRulesConfig `yaml:"baz,omitempty" json:"baz,omitempty" toml:"baz,omitempty"`
}

RulesConfig controls how rules are written to generated outputs.

type RulesDirOwner

type RulesDirOwner interface {
	// SplitRulesDir returns the folder, or "" when the generator has none.
	SplitRulesDir() string
}

RulesDirOwner is implemented by preset generators that write one native rule file per rule into a folder beyond the built-in rules folders (a custom provider spec with a split rules output). The folder gets the protections the built-in ones have.

type RulesDirSet

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

RulesDirSet is the set of rules folders of one project: the built-in ones plus the folders custom provider specs write rule files into. The custom folders come from the project's own config, so they belong to its Config and are never shared with another project in the same process. A nil *RulesDirSet is the built-in set.

func (*RulesDirSet) Add

func (s *RulesDirSet) Add(dir string)

Add makes dir a rules folder of this project, so that files a custom provider spec writes there (outputs.rules with split = true) get the same protections as the built-in folders: the overwrite guard, hashes in the banner and per-file gitignore entries. Adding the same folder twice is a no-op.

func (*RulesDirSet) In

func (s *RulesDirSet) In(relPath string) bool

In reports whether relPath is a file inside a rules folder.

func (*RulesDirSet) Remainder

func (s *RulesDirSet) Remainder(relPath string) (rest string, ok bool)

Remainder reports whether relPath is a rules folder, or something inside one, at the repo root or nested under a subproject. rest is the path relative to the rules folder ("" for the folder itself).

type RulesFolderKind

type RulesFolderKind int

RulesFolderKind describes what a preset's rules folder holds beside the shared AGENTS.md, which decides what AGENTS.md must carry for it.

const (
	// RulesFolderNone: the preset has no rules folder, so AGENTS.md carries
	// everything for it.
	RulesFolderNone RulesFolderKind = iota
	// RulesFolderAlways: every item that is not always-on becomes a file, in
	// both rules modes (cursor, devin, cline).
	RulesFolderAlways
	// RulesFolderSplitOnly: files only in the "split" rules mode; in "inline"
	// mode the preset writes none and AGENTS.md carries the scoped items (junie).
	RulesFolderSplitOnly
	// RulesFolderScopedInInline: in "split" mode every non-always-on item is a
	// file; in "inline" mode only glob-scoped ones stay files (claude through
	// inline_filter = path_scoped, antigravity through RoutingScopedOnly), so
	// AGENTS.md carries the auto and manual items.
	RulesFolderScopedInInline
	// RulesFolderScopedOnly: only glob-scoped items are files in either mode;
	// auto and manual items have no file the tool applies (copilot).
	RulesFolderScopedOnly
)

Rules folder kinds.

type RunState

type RunState struct {
	// Scope is the scope being generated; nil for the project root.
	Scope *ScopeRun
	// contains filtered or unexported fields
}

RunState is the per-generation state shared by the root run and the scope runs. A config used by one run carries it in Config.Run.

func NewRunState

func NewRunState() *RunState

NewRunState returns the state of a generation, positioned at the project root.

func (*RunState) ClaimsFor

func (r *RunState) ClaimsFor(preset string) *PathClaims

ClaimsFor returns the PathClaims of a preset, creating it on first use.

func (*RunState) ForScope

func (r *RunState) ForScope(scope *ScopeRun) *RunState

ForScope returns the state for generating scope. It shares path claims with the receiver.

func (*RunState) PreviousClaims

func (r *RunState) PreviousClaims(rel string) []jsonmerge.Claim

PreviousClaims returns what the previous run recorded as ai-rulez's in the merged JSON document at the project-relative path.

func (*RunState) SetPreviousMerged

func (r *RunState) SetPreviousMerged(merged map[string][]jsonmerge.Claim)

SetPreviousMerged records the ownership claims of the previous run.

func (*RunState) SetPreviouslyGenerated

func (r *RunState) SetPreviouslyGenerated(files []string)

SetPreviouslyGenerated records the files the previous run generated.

func (*RunState) WasGenerated

func (r *RunState) WasGenerated(rel string) bool

WasGenerated reports whether the previous run wrote the project-relative file whole, so its content is ai-rulez's rather than hand-authored.

type SchemaFinding

type SchemaFinding struct {
	// File is the configuration file the finding is in.
	File string
	// Path is the table the finding is in ("" for the document root, "lint" for [lint]).
	Path string
	// Key is the unknown key, "" for any other kind of finding.
	Key string
	// Suggestion is the nearest known key of Path, "" when none is close.
	Suggestion string
	// TopLevel is set when Key is a known top-level key found inside the table
	// Path: it was written after a [table] header and so belongs to that table.
	TopLevel bool
	// Message describes a finding that is not an unknown key.
	Message string
}

SchemaFinding is one problem the JSON schema reports in the configuration or its machine-local overlay. validate fails on the first of them; generate reports them all as warnings, or fails with --strict.

func SchemaFindings

func SchemaFindings(cfg *Config) ([]SchemaFinding, error)

SchemaFindings runs the same schema validation as `ai-rulez validate` (schema.ValidateFile for the configuration, schema.ValidateLocalFile for the machine-local overlay) and returns every finding. An error is returned only when a file could not be read at all.

func (SchemaFinding) String

func (f SchemaFinding) String() string

String is the one-line text printed for the finding.

type ScopeConfig

type ScopeConfig struct {
	Name    string   `yaml:"name,omitempty" json:"name,omitempty" toml:"name,omitempty"`
	Path    string   `yaml:"path" json:"path" toml:"path"`
	Profile string   `yaml:"profile,omitempty" json:"profile,omitempty" toml:"profile,omitempty"`
	Presets []string `yaml:"presets,omitempty" json:"presets,omitempty" toml:"presets,omitempty"`
}

ScopeConfig configures an additional scoped output root for directory-aware assistants such as Codex and Claude Code.

type ScopeRun

type ScopeRun struct {
	Path    string // scope path relative to the project root, slash form
	Slug    string // file-name qualifier derived from Path
	RootDir string // absolute project root
}

ScopeRun describes the monorepo scope being generated. Rules of a scope are written into the root rules folders, so presets need the project root as well as the scope.

type SessionStartConfig added in v5.5.0

type SessionStartConfig struct {
	// Generate, when false, removes the hook (and with it the prompt). Nil means on.
	Generate *bool `yaml:"generate,omitempty" json:"generate,omitempty" toml:"generate,omitempty"`
	// When is "remote" (default: act only when CLAUDE_CODE_REMOTE is set) or "always".
	When string `yaml:"when,omitempty" json:"when,omitempty" toml:"when,omitempty"`
	// Mode is "auto" (default: --locked when ai-rulez.lock exists, else plain),
	// "locked", "frozen" or "plain".
	Mode string `yaml:"mode,omitempty" json:"mode,omitempty" toml:"mode,omitempty"`
	// Timeout bounds the whole run, in seconds. Zero means 120.
	Timeout int `yaml:"timeout,omitempty" json:"timeout,omitempty" toml:"timeout,omitempty"`
	// Version pins the ai-rulez version the hook runs through npx and uvx. Empty
	// means the latest published release.
	Version string `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	// Command replaces the whole launcher (executable followed by its arguments,
	// without the trailing "session-start"), for example ["ai-rulez"]. It runs
	// without a shell. Version is ignored when it is set.
	Command []string `yaml:"command,omitempty" json:"command,omitempty" toml:"command,omitempty"`
	// Prompt, when false, leaves the context injection out. Nil means on.
	Prompt *bool `yaml:"prompt,omitempty" json:"prompt,omitempty" toml:"prompt,omitempty"`
	// PromptText replaces the default context. {{server}} is the ai-rulez MCP server's name.
	PromptText string `yaml:"prompt_text,omitempty" json:"prompt_text,omitempty" toml:"prompt_text,omitempty"`
}

SessionStartConfig configures the built-in SessionStart hook: in a remote (cloud) agent session it runs `ai-rulez session-start`, which regenerates the outputs of the fresh clone and tells the agent how the configuration is managed. It is a table of its own because `[hooks]` cannot coexist with the `[[hooks]]` array of tables in TOML.

func (*SessionStartConfig) GenerateOn added in v5.5.0

func (s *SessionStartConfig) GenerateOn() bool

GenerateOn reports whether the hook is on; it is unless generate = false.

func (*SessionStartConfig) ModeOrDefault added in v5.5.0

func (s *SessionStartConfig) ModeOrDefault() string

ModeOrDefault is the effective [session_start] mode.

func (*SessionStartConfig) PromptOn added in v5.5.0

func (s *SessionStartConfig) PromptOn() bool

PromptOn reports whether the context injection is on; it is unless prompt = false.

func (*SessionStartConfig) TimeoutSecondsOrDefault added in v5.5.0

func (s *SessionStartConfig) TimeoutSecondsOrDefault() int

TimeoutSecondsOrDefault is the effective run timeout in seconds.

func (*SessionStartConfig) WhenOrDefault added in v5.5.0

func (s *SessionStartConfig) WhenOrDefault() string

WhenOrDefault is the effective [session_start] when.

type SessionStartLauncher added in v5.5.0

type SessionStartLauncher struct {
	Name    string // path, npx or uvx
	Command string // the command line, with the version it pins
}

SessionStartLauncher is one way the hook starts ai-rulez, in the order it tries them.

type SharedOutput

type SharedOutput string

SharedOutput names an output several presets can read and that the agents_md flag therefore renders once instead of once per preset.

const (
	// SharedAgentsMD is the root AGENTS.md (nested <scope>/AGENTS.md for scopes).
	SharedAgentsMD SharedOutput = "AGENTS.md"
	// SharedAgentSkills is the .agents/skills directory of Agent Skills.
	SharedAgentSkills SharedOutput = ".agents/skills"
)

type SharedOutputConsumer

type SharedOutputConsumer struct {
	Outputs []SharedOutput
	// OwnSkillsDir is the preset's own skills directory, relative to the output
	// base dir. Empty when the preset has none or already writes .agents/skills.
	OwnSkillsDir string
	// OwnRootFile is the preset's own root instruction file, relative to the
	// output base dir, that it stops writing because AGENTS.md replaces it
	// (GEMINI.md, .hermes.md). The preset skips rendering it. Empty when the
	// preset keeps its root file.
	OwnRootFile string
	// ImportsAgentsMD marks a preset that does not read AGENTS.md itself and
	// instead imports it from its own root file (CLAUDE.md with "@AGENTS.md").
	// Such a preset still needs the shared AGENTS.md rendered, and owns it for
	// frontmatter targets.
	ImportsAgentsMD bool
	// CommandsAsSkills marks a preset that has no command folder and runs a
	// command as a skill: the shared .agents/skills tree carries the commands.
	CommandsAsSkills bool
	// Folder says what the preset's own rules folder holds once the shared
	// AGENTS.md carries the always-on items; see RulesFolderKind.
	Folder RulesFolderKind
}

SharedOutputConsumer describes how a preset takes part in the shared outputs when agents_md is on: which ones it reads, and the preset-specific skills directory it stops writing because .agents/skills replaces it.

func SharedOutputConsumerFor

func SharedOutputConsumerFor(preset string) (SharedOutputConsumer, bool)

SharedOutputConsumerFor returns the shared-output participation of a built-in preset, and false when the preset takes no part.

func (SharedOutputConsumer) HasRootFile

func (c SharedOutputConsumer) HasRootFile() bool

HasRootFile reports whether the preset has a root instructions file that the shared AGENTS.md stands in for: dropped (GEMINI.md, .hermes.md, ...) or turned into an import shim (CLAUDE.md).

func (SharedOutputConsumer) NeedsAgentsMD

func (c SharedOutputConsumer) NeedsAgentsMD() bool

NeedsAgentsMD reports whether the preset reads or imports the shared AGENTS.md.

func (SharedOutputConsumer) Reads

func (c SharedOutputConsumer) Reads(output SharedOutput) bool

Reads reports whether the consumer reads the shared output.

func (SharedOutputConsumer) ReplacedRootFile

func (c SharedOutputConsumer) ReplacedRootFile() string

ReplacedRootFile is that root file, relative to the output base dir, or "" when the preset has none.

type SharedResource added in v5.0.4

type SharedResource struct {
	Name string
	Dir  string
}

SharedResource is a resolved plugin shared_resources directory: its base name (the subdirectory copied into every skill) and its absolute source directory.

type SigningConfig

type SigningConfig struct {
	// Require lists the subjects that must carry a valid attestation: "lock"
	// (`lock --check` and `generate --locked` fail without one), "served" (the
	// MCP skill server refuses a skill the signed lock does not pin) and "skill"
	// (it refuses a remote skill without a trusted publisher attestation).
	Require []string `yaml:"require,omitempty" json:"require,omitempty" toml:"require,omitempty"`
	// TLog is "required", "optional" or "off". Default: required when a trusted
	// signer is a certificate identity, off when only keys are trusted.
	TLog string `yaml:"tlog,omitempty" json:"tlog,omitempty" toml:"tlog,omitempty"`
	// MaxAge is the oldest acceptable signature ("180d", "720h"); empty is no limit.
	MaxAge string `yaml:"max_age,omitempty" json:"max_age,omitempty" toml:"max_age,omitempty"` //nolint:tagliatelle
	// TrustedRoot is a Sigstore trusted root file inside the project (keyless
	// verification). Default: the root `ai-rulez trust update` cached for the user.
	TrustedRoot string `yaml:"trusted_root,omitempty" json:"trusted_root,omitempty" toml:"trusted_root,omitempty"` //nolint:tagliatelle
	// MinHashVersion rejects an attestation of an older hash scheme.
	MinHashVersion int `yaml:"min_hash_version,omitempty" json:"min_hash_version,omitempty" toml:"min_hash_version,omitempty"` //nolint:tagliatelle
	// Attestation is the lock bundle path relative to the config directory.
	// Default ai-rulez.lock.sigstore.json.
	Attestation string `yaml:"attestation,omitempty" json:"attestation,omitempty" toml:"attestation,omitempty"`

	// Identity and Issuer are the shorthand for one trusted keyless signer.
	Identity string `yaml:"identity,omitempty" json:"identity,omitempty" toml:"identity,omitempty"`
	Issuer   string `yaml:"issuer,omitempty" json:"issuer,omitempty" toml:"issuer,omitempty"`
	// KeyFile is the shorthand for one trusted public key (PEM, inside the project).
	KeyFile string `yaml:"key_file,omitempty" json:"key_file,omitempty" toml:"key_file,omitempty"` //nolint:tagliatelle

	// Trust is the full form: scoped, repeatable trust entries.
	Trust []SigningTrust `yaml:"trust,omitempty" json:"trust,omitempty" toml:"trust,omitempty"`

	// Thresholds sets, per subject, how many distinct trusted signers must have
	// signed it (k of n); the default is 1. The signatures are separate bundle files.
	Thresholds map[string]int `yaml:"thresholds,omitempty" json:"thresholds,omitempty" toml:"thresholds,omitempty"`
	// RequireProvenance makes verifying a bundle demand a signed SLSA provenance
	// statement next to it.
	RequireProvenance bool `yaml:"require_provenance,omitempty" json:"require_provenance,omitempty" toml:"require_provenance,omitempty"` //nolint:tagliatelle
	// Builders, when set, are the only builder ids a provenance statement may name.
	Builders []string `yaml:"builders,omitempty" json:"builders,omitempty" toml:"builders,omitempty"`
}

SigningConfig is the [signing] table: who may sign the lock and how fresh the signature must be. Nothing is enforced unless require names a subject; signing itself is always opt-in.

func (*SigningConfig) Requires

func (s *SigningConfig) Requires(subject string) bool

Requires reports whether subject must carry a valid attestation.

func (*SigningConfig) SigningTLog

func (s *SigningConfig) SigningTLog() string

SigningTLog returns the effective tlog mode: the configured one, else "required" when any trusted signer is a certificate identity, else "off".

func (*SigningConfig) SigningTrustEntries

func (s *SigningConfig) SigningTrustEntries() []SigningTrust

SigningTrustEntries returns the shorthand as a trust entry followed by the [[signing.trust]] entries, with the default subject filled in.

type SigningTrust

type SigningTrust struct {
	// Subject is what the signer may vouch for: lock, bundle, skill or sbom;
	// default "lock".
	Subject string `yaml:"subject,omitempty" json:"subject,omitempty" toml:"subject,omitempty"`
	// Source scopes a skill entry to one [[skill_sources]] or [[installed_skills]]
	// name, so a publisher trusted for one source cannot vouch for another.
	Source string `yaml:"source,omitempty" json:"source,omitempty" toml:"source,omitempty"`
	// Identity is matched exactly against the certificate subject alternative name.
	Identity string `yaml:"identity,omitempty" json:"identity,omitempty" toml:"identity,omitempty"`
	// IdentityRegexp is matched against the whole identity and must be anchored (^...$).
	IdentityRegexp string `yaml:"identity_regexp,omitempty" json:"identity_regexp,omitempty" toml:"identity_regexp,omitempty"` //nolint:tagliatelle
	// Issuer is matched exactly against the OIDC issuer extension.
	Issuer string `yaml:"issuer,omitempty" json:"issuer,omitempty" toml:"issuer,omitempty"`
	// KeyFile is a PEM public key inside the project.
	KeyFile string `yaml:"key_file,omitempty" json:"key_file,omitempty" toml:"key_file,omitempty"` //nolint:tagliatelle
	// Reviewer names the person a key_file belongs to (an email, "github:login" or
	// "@login"), so [governance] forbid_self_approval can tell whether the signer
	// wrote the change: a key has no identity of its own.
	Reviewer string `yaml:"reviewer,omitempty" json:"reviewer,omitempty" toml:"reviewer,omitempty"`
	// ValidFrom and ValidUntil ("2026-01-01" or RFC 3339) bound the signing time
	// the entry accepts; an expiry forces a reviewed renewal.
	ValidFrom  string `yaml:"valid_from,omitempty" json:"valid_from,omitempty" toml:"valid_from,omitempty"`    //nolint:tagliatelle
	ValidUntil string `yaml:"valid_until,omitempty" json:"valid_until,omitempty" toml:"valid_until,omitempty"` //nolint:tagliatelle
}

SigningTrust is one [[signing.trust]] entry: a certificate identity (Identity or IdentityRegexp, with Issuer) or a public key (KeyFile).

type SkillBundleLimitError added in v5.5.0

type SkillBundleLimitError struct {
	Dir   string
	Limit string
	// Entry is set when Limit is the cap on all the skills of one entry.
	Entry bool
}

SkillBundleLimitError is returned when an installed skill bundles more than the per-skill file or byte cap, or an entry's skills more than the entry cap.

func (*SkillBundleLimitError) Error added in v5.5.0

func (e *SkillBundleLimitError) Error() string

type SkillResource

type SkillResource struct {
	// Kind is one of "references", "scripts", "assets".
	Kind string
	// RelPath is the resource path relative to the skill root, including the
	// kind subdirectory (e.g. "references/api.md").
	RelPath string
	// Content holds the raw bytes of the file. Bytes (not string) so binary
	// assets round-trip without UTF-8 corruption.
	Content []byte
	// Mode is the file permission bits read from disk. Preserved through
	// generation so bundled scripts keep their executable bit.
	Mode os.FileMode
	// Description is parsed from a reference file's frontmatter `description`
	// field, or falls back to the first non-empty markdown line. Empty for
	// scripts and assets, or when no description is available.
	Description string
}

SkillResource is one supporting file bundled with a skill.

The canonical Agent Skills layout (followed by Claude Code, OpenAI Codex, and the agentskills.io standard) places these under three subdirectories:

references/  — markdown docs the agent reads on demand
scripts/     — executable scripts the agent invokes
assets/      — files used in output (templates, images, etc.)

Resources are emitted as individual files under the rendered skill directory and indexed from SKILL.md so the agent can discover them.

func LoadAllSkillFiles added in v5.5.0

func LoadAllSkillFiles(ctx context.Context, skillDir string, extraExcludes []string) ([]SkillResource, error)

LoadAllSkillFiles loads every regular file below skillDir besides SKILL.md, whatever directory it sits in (the root, references/, a folder of its own). It applies the limits of an installed skill: files git ignores, the bundle excludes (DefaultBundleExcludes and extraExcludes), symbolic links, credential names, files over 8 MiB and the 2000 file / 16 MiB total are left out or, for the total, refused with a SkillBundleLimitError. The external scanners stage a skill with it.

func LoadResources

func LoadResources(root, itemKind string) ([]SkillResource, error)

LoadResources is LoadResourcesWith with only the default bundle excludes.

func LoadResourcesWith

func LoadResourcesWith(root, itemKind string, extraExcludes []string) ([]SkillResource, error)

LoadResources walks a skill or command directory and returns its supporting files from references/, scripts/, and assets/ subdirectories. Files are read as raw bytes so binary assets round-trip without UTF-8 corruption.

For markdown references the function also extracts a short description, preferring the frontmatter `description` field, falling back to the first non-empty content line (with markdown heading markers stripped).

Missing subdirectories are not an error — they simply contribute no resources. Walk failures within a subdirectory are returned to the caller so the loader can decide whether to surface or warn.

Unrecognized subdirectories (any directory other than references/, scripts/, assets/ or evals/) trigger a warning naming the item and the offending directory. The Agent Skills spec defines only these three as canonical resource kinds.

Build artifacts are not bundled: files git ignores (when root is in a git work tree), anything matching DefaultBundleExcludes (.venv*, venv, __pycache__, *.pyc, node_modules, .git) and anything matching extraExcludes (the bundle_exclude config key) are skipped.

itemKind is one of the ItemKind constants and selects the entry file name (SKILL.md, COMMAND.md) and the diagnostics wording.

func LoadSkillResources

func LoadSkillResources(skillDir string) ([]SkillResource, error)

LoadSkillResources loads the resources of a skill directory. Thin wrapper over LoadResources for the common case.

func LoadSkillResourcesContext

func LoadSkillResourcesContext(ctx context.Context, skillDir string) ([]SkillResource, error)

LoadSkillResourcesContext is LoadSkillResources reporting through the logger ctx carries (logger.WithContext) and asking the VCS, to leave out ignored files, through the runner ctx carries.

type SkillSourceConfig

type SkillSourceConfig struct {
	// Name identifies the source in the lock and in provenance.
	Name string `yaml:"name" json:"name" toml:"name"`
	// URL is a git URL (https, ssh, file) or a local directory path.
	URL string `yaml:"url" json:"url" toml:"url"`
	// Ref is a tag or a full commit SHA. A branch (or none) follows a moving
	// ref and is reported as unpinned (AR010).
	Ref string `yaml:"ref,omitempty" json:"ref,omitempty" toml:"ref,omitempty"`
	// Version is a semver constraint resolved against the repository's tags
	// (see VersionSpec); it excludes Ref. TagPrefix and IncludePrerelease refine it.
	Version           string `yaml:"version,omitempty" json:"version,omitempty" toml:"version,omitempty"`
	TagPrefix         string `yaml:"tag_prefix,omitempty" json:"tag_prefix,omitempty" toml:"tag_prefix,omitempty"`                         //nolint:tagliatelle
	IncludePrerelease bool   `yaml:"include_prerelease,omitempty" json:"include_prerelease,omitempty" toml:"include_prerelease,omitempty"` //nolint:tagliatelle
	MinReleaseAge     string `yaml:"min_release_age,omitempty" json:"min_release_age,omitempty" toml:"min_release_age,omitempty"`          //nolint:tagliatelle
	// Path is the subdirectory of the repository that holds skill directories.
	Path string `yaml:"path,omitempty" json:"path,omitempty" toml:"path,omitempty"`
	// Include keeps only skills whose directory name matches one of these globs.
	Include []string `yaml:"include,omitempty" json:"include,omitempty" toml:"include,omitempty"`
	// Exclude drops skills whose directory name matches one of these globs; it wins over Include.
	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty" toml:"exclude,omitempty"`
	// NamePrefix is prepended to every served skill name, so two sources cannot collide.
	NamePrefix string `yaml:"name_prefix,omitempty" json:"name_prefix,omitempty" toml:"name_prefix,omitempty"` //nolint:tagliatelle
	// Trust is the scan level: "error" (default) or "warn". See TrustError.
	Trust string `yaml:"trust,omitempty" json:"trust,omitempty" toml:"trust,omitempty"`
	// Domain restricts this source to the named domain: it is served only when
	// that domain is selected by the active profile or role. Empty means global.
	Domain string `yaml:"domain,omitempty" json:"domain,omitempty" toml:"domain,omitempty"`
	// MaxSkills caps how many skills the source may load (0 selects the default of 200).
	MaxSkills int `yaml:"max_skills,omitempty" json:"max_skills,omitempty" toml:"max_skills,omitempty"` //nolint:tagliatelle
	// MaxBytes caps the total size of the files the source loads (0 selects the default of 64 MiB).
	MaxBytes int `yaml:"max_bytes,omitempty" json:"max_bytes,omitempty" toml:"max_bytes,omitempty"` //nolint:tagliatelle
	// MaxCloneBytes caps what a git source may download and check out (0 selects
	// the global limit, AI_RULEZ_MAX_CLONE_BYTES, or the default of 256 MiB).
	MaxCloneBytes int64 `yaml:"max_clone_bytes,omitempty" json:"max_clone_bytes,omitempty" toml:"max_clone_bytes,omitempty"` //nolint:tagliatelle
	// MaxCloneFiles caps the number of entries of a git clone (0 selects
	// AI_RULEZ_MAX_CLONE_FILES or the default of 20000).
	MaxCloneFiles int `yaml:"max_clone_files,omitempty" json:"max_clone_files,omitempty" toml:"max_clone_files,omitempty"` //nolint:tagliatelle
}

SkillSourceConfig is one [[skill_sources]] entry: a git repository or local directory whose skills are served, never written to the static trees.

func (*SkillSourceConfig) GetPath added in v5.0.2

func (s *SkillSourceConfig) GetPath() string

GetPath returns the subdirectory of the source that holds its skill directories. An entry that selects several skills with include/exclude and names no path is a directory of skill directories: it defaults to the conventional "skills" directory, so `name` is only the entry's label. Without include/exclude, an empty path means the source root.

func (*SkillSourceConfig) RequestedRef

func (s *SkillSourceConfig) RequestedRef() string

RequestedRef is what the lock records as the requested ref.

func (*SkillSourceConfig) Validate

func (s *SkillSourceConfig) Validate(index int) error

Validate checks one [[skill_sources]] entry; index is used in messages only.

func (*SkillSourceConfig) VersionSpec

func (s *SkillSourceConfig) VersionSpec() VersionSpec

VersionSpec returns the version constraint of the skill source.

type SkillsConfig

type SkillsConfig struct {
	// Delivery is the global default delivery: static (default), served or both.
	Delivery string `yaml:"delivery,omitempty" json:"delivery,omitempty" toml:"delivery,omitempty"`
}

SkillsConfig is the [skills] table: defaults that apply to every skill.

type SourceChecker added in v5.5.0

type SourceChecker interface {
	CheckSource(cfg *Config, kind, name, source string) error
}

SourceChecker is a PolicyEnforcer that can judge one source at the time it is fetched. A marketplace names the repositories of its plugins only once its index is read, so the host rules of the policy are applied to them then.

type Statusline

type Statusline struct {
	Script  string `yaml:"script" json:"script" toml:"script"`                                  // path to the (hand-authored) statusline script
	Command string `yaml:"command,omitempty" json:"command,omitempty" toml:"command,omitempty"` // enabling slash-command name
}

Statusline declares a Claude-only status-line script passthrough.

type StubConfig added in v5.0.4

type StubConfig struct {
	// Description replaces the frontmatter description, which every session
	// lists, so a shorter one saves tokens.
	Description string `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	// Body replaces the stub's text. {{server}} is the MCP server that serves
	// the skills. It must still tell the agent to call find_skill and load_skill.
	Body string `yaml:"body,omitempty" json:"body,omitempty" toml:"body,omitempty"`
	// BodyFile is Body read from a file, relative to the config directory.
	// Body and BodyFile are mutually exclusive.
	BodyFile string `yaml:"body_file,omitempty" json:"body_file,omitempty" toml:"body_file,omitempty"`
}

StubConfig is [delivery.stub]: the name and text of the stub skill that tells an MCP-capable agent to call find_skill and load_skill. Every field is optional; an unset one keeps the built-in text.

type TelemetryConfig

type TelemetryConfig struct {
	// Enabled is a pointer so the user config can say false explicitly: an
	// explicit user-scope false beats a repository's true.
	Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty" toml:"enabled,omitempty"`
	// AllowNetwork is the explicit consent for OTLP export. User scope only.
	AllowNetwork bool `yaml:"allow_network,omitempty" json:"allow_network,omitempty" toml:"allow_network,omitempty"` //nolint:tagliatelle
	// OTLPEndpoint is the collector base URL ("https://collector:4318"); for the
	// HTTP protocols /v1/logs and /v1/metrics are appended, for grpc it names
	// host[:port] (default port 4317) and carries no path. User scope only.
	OTLPEndpoint string `yaml:"otlp_endpoint,omitempty" json:"otlp_endpoint,omitempty" toml:"otlp_endpoint,omitempty"` //nolint:tagliatelle
	// OTLPProtocol is "http/json" (the default), "http/protobuf" or "grpc".
	OTLPProtocol string `yaml:"otlp_protocol,omitempty" json:"otlp_protocol,omitempty" toml:"otlp_protocol,omitempty"` //nolint:tagliatelle
	// HeadersEnv names environment variables whose values are "k=v,k2=v2" header
	// lists. Names only: a literal credential is rejected. User scope only.
	HeadersEnv []string `yaml:"headers_env,omitempty" json:"headers_env,omitempty" toml:"headers_env,omitempty"` //nolint:tagliatelle
	// ServiceName is the service.name resource attribute (default "ai-rulez"). User
	// scope only: a repository value is ignored (it would land on the user's collector).
	ServiceName string `yaml:"service_name,omitempty" json:"service_name,omitempty" toml:"service_name,omitempty"` //nolint:tagliatelle
	// Sample is the fraction of sessions exported, 0..1 (default 1). Sampling is
	// per salted session hash, so one session is wholly in or out.
	Sample *float64 `yaml:"sample,omitempty" json:"sample,omitempty" toml:"sample,omitempty"`
	// IncludePaths adds the repo-relative path of a loaded item to exported
	// events. Default false. User scope only.
	IncludePaths bool `yaml:"include_paths,omitempty" json:"include_paths,omitempty" toml:"include_paths,omitempty"` //nolint:tagliatelle
	// IncludeSession adds the salted session hash to exported log records (never
	// to metric labels). Default false: it is pseudonymous. User scope only.
	IncludeSession bool `yaml:"include_session,omitempty" json:"include_session,omitempty" toml:"include_session,omitempty"` //nolint:tagliatelle
	// SaltFile is the file holding the session-hash salt. User scope only.
	SaltFile string `yaml:"salt_file,omitempty" json:"salt_file,omitempty" toml:"salt_file,omitempty"` //nolint:tagliatelle
	// Resource adds fixed labels (a team, an environment) to the OTLP resource, so
	// a collector can group the data without a processor. User scope only: a
	// repository value would label data on the user's collector. Keys are
	// lower-case dotted names, at most TelemetryResourceMaxEntries entries of at
	// most TelemetryResourceMaxValue characters; the service.*, host.*, user.*,
	// process.*, os.* and ai_rulez.* namespaces are reserved so host or user data
	// cannot be added.
	Resource map[string]string `yaml:"resource,omitempty" json:"resource,omitempty" toml:"resource,omitempty"`
}

TelemetryConfig configures item-load telemetry: identifier-only events about which skills, rules, agents and context files a harness loaded. Everything is off by default. Local JSONL recording needs only Enabled; network export also needs AllowNetwork and an endpoint, and a repository config cannot grant either (see internal/telemetry and docs/telemetry.md for the trust rule).

func (*TelemetryConfig) Validate

func (t *TelemetryConfig) Validate() []string

Validate returns one message per problem in the telemetry settings; nil when the section is valid. It checks shape only: the trust rule (which scope may set what) is applied by internal/telemetry.

type Template

type Template struct {
	Type  TemplateType `yaml:"type"`
	Value string       `yaml:"value"`
}

func ParseTemplate

func ParseTemplate(templateConfig TemplateConfig) (*Template, error)

func (*Template) IsBuiltin

func (t *Template) IsBuiltin() bool

func (*Template) IsFile

func (t *Template) IsFile() bool

func (*Template) IsInline

func (t *Template) IsInline() bool

func (*Template) String

func (t *Template) String() string

type TemplateConfig

type TemplateConfig interface{}

type TemplateType

type TemplateType string
const (
	TemplateBuiltin TemplateType = "builtin"
	TemplateFile    TemplateType = "file"
	TemplateInline  TemplateType = "inline"
)

type UsageConfig

type UsageConfig struct {
	// SkillsIndex makes `generate` write <config dir>/skills-index.json: one record per
	// generated skill with its stable id, source path, content hash, owner,
	// version and the outputs written for each preset. The usage hook and
	// `ai-rulez telemetry report` join a usage log against it.
	SkillsIndex bool `yaml:"skills_index,omitempty" json:"skills_index,omitempty" toml:"skills_index,omitempty"` //nolint:tagliatelle
}

UsageConfig configures usage-telemetry support. Everything here is off by default and never makes a network call.

type VerifierConfig

type VerifierConfig struct {
	// Name identifies the verifier; it is unique and is the merge key of the
	// machine-local overlay.
	Name        string `yaml:"name" json:"name" toml:"name"`
	Description string `yaml:"description,omitempty" json:"description,omitempty" toml:"description,omitempty"`
	// Type selects the predicate; see VerifierTypes. Empty selects the spec form.
	Type string `yaml:"type,omitempty" json:"type,omitempty" toml:"type,omitempty"`
	// Severity is error (default), warning or info. Only errors (and warnings
	// with --strict) fail the run.
	Severity string `yaml:"severity,omitempty" json:"severity,omitempty" toml:"severity,omitempty"`
	// Path is a repo-relative file for file_exists, file_absent and key_equals.
	Path string `yaml:"path,omitempty" json:"path,omitempty" toml:"path,omitempty"`
	// Glob selects files for glob_count, regex and forbid; Exclude removes matches.
	Glob    string   `yaml:"glob,omitempty" json:"glob,omitempty" toml:"glob,omitempty"`
	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty" toml:"exclude,omitempty"`
	// Pattern is the RE2 expression for regex and forbid.
	Pattern string `yaml:"pattern,omitempty" json:"pattern,omitempty" toml:"pattern,omitempty"`
	// Min and Max bound the match count of glob_count.
	Min *int `yaml:"min,omitempty" json:"min,omitempty" toml:"min,omitempty"`
	Max *int `yaml:"max,omitempty" json:"max,omitempty" toml:"max,omitempty"`
	// Key is a dotted path into a JSON, YAML or TOML document (key_equals);
	// Equals is the expected scalar, compared as text.
	Key    string  `yaml:"key,omitempty" json:"key,omitempty" toml:"key,omitempty"`
	Equals *string `yaml:"equals,omitempty" json:"equals,omitempty" toml:"equals,omitempty"`
	// Profile is the generate profile generated_in_sync renders ("" selects the default).
	Profile string `yaml:"profile,omitempty" json:"profile,omitempty" toml:"profile,omitempty"`

	// Spec form (Type empty): exactly one of Rule, Skill, Agent and Command
	// names the enforced item, as `id` or `domain/id`.
	Rule    string `yaml:"rule,omitempty" json:"rule,omitempty" toml:"rule,omitempty"`
	Skill   string `yaml:"skill,omitempty" json:"skill,omitempty" toml:"skill,omitempty"`
	Agent   string `yaml:"agent,omitempty" json:"agent,omitempty" toml:"agent,omitempty"`
	Command string `yaml:"command,omitempty" json:"command,omitempty" toml:"command,omitempty"`
	// Anchor is a heading of the enforced file; findings point at its line.
	Anchor  string `yaml:"anchor,omitempty" json:"anchor,omitempty" toml:"anchor,omitempty"`
	Message string `yaml:"message,omitempty" json:"message,omitempty" toml:"message,omitempty"`
	Fix     string `yaml:"fix,omitempty" json:"fix,omitempty" toml:"fix,omitempty"`
	// WhenChanged scopes the verifier to changed files matching these globs;
	// Exclude (shared with the flat form) removes matches.
	WhenChanged []string `yaml:"when_changed,omitempty" json:"when_changed,omitempty" toml:"when_changed,omitempty"`
	// Require is the predicate tree: one predicate or an all/any/not combinator.
	Require  *vspec.Require  `yaml:"require,omitempty" json:"require,omitempty" toml:"require,omitempty"`
	Examples []vspec.Example `yaml:"examples,omitempty" json:"examples,omitempty" toml:"examples,omitempty"`
}

VerifierConfig is one deterministic repo check declared as [[verifiers]] and run by `ai-rulez verifiers run`. Two forms share the table. The flat form sets Type, and the fields that type uses. The spec form leaves Type empty and declares what a .ai-rulez/verifiers/*.toml entry does (the enforced rule, skill, agent or command, WhenChanged, Require, Fix, Examples); Name is its id.

func (*VerifierConfig) IsSpec

func (v *VerifierConfig) IsSpec() bool

IsSpec reports whether the entry uses the spec form (no Type).

type VerifiersSettings

type VerifiersSettings struct {
	// MaxTimeoutS caps the timeout_s of a command predicate (default 300).
	MaxTimeoutS int `yaml:"max_timeout_s,omitempty" json:"max_timeout_s,omitempty" toml:"max_timeout_s,omitempty"`
	// MaxFileBytes bounds how much of a file a content predicate reads
	// (default 5 MiB); a larger file is skipped with a note, never half-read.
	MaxFileBytes int `yaml:"max_file_bytes,omitempty" json:"max_file_bytes,omitempty" toml:"max_file_bytes,omitempty"`
	// RequireExamples reports a spec verifier without self-test examples (AR9H6).
	RequireExamples bool `yaml:"require_examples,omitempty" json:"require_examples,omitempty" toml:"require_examples,omitempty"`
	// WarnDead reports a verifier whose when_changed matches no file (AR9H5)
	// on every run, as --strict-applicability does.
	WarnDead bool `yaml:"warn_dead,omitempty" json:"warn_dead,omitempty" toml:"warn_dead,omitempty"`
	// TrustExecFrom names includes whose verifiers may use the command
	// predicate. The include must also be pinned in ai-rulez.lock.
	TrustExecFrom []string `yaml:"trust_exec_from,omitempty" json:"trust_exec_from,omitempty" toml:"trust_exec_from,omitempty"`
	// CommandEnv lists extra environment variable NAMES passed to command
	// predicates. Everything else is scrubbed; credential-looking names are refused.
	CommandEnv []string `yaml:"command_env,omitempty" json:"command_env,omitempty" toml:"command_env,omitempty"`
}

VerifiersSettings is the [verifiers_settings] table: limits and policy of `ai-rulez verifiers run`. (The name differs from the [[verifiers]] array because TOML cannot use one key as both a table and an array.)

type VersionPolicy

type VersionPolicy struct {
	// Advance selects the sources a refresh moves to the newest allowed tag.
	// nil moves none: a pin that still satisfies its constraint is kept.
	Advance func(kind, name string) bool
	// AllowDowngrade lets Advance select a tag with lower precedence than the pin.
	AllowDowngrade bool
	// AcceptMovedTag re-pins a tag that now points to another commit instead of
	// failing with AR732.
	AcceptMovedTag bool
	// ReleaseGate returns the minimum release age gate for a source, or nil for
	// none. It is only consulted when a range is resolved to a new tag (a pin
	// that still satisfies its constraint is kept without a lookup, its age
	// having been decided when it was pinned).
	ReleaseGate func(w lockfile.Want) *tagresolve.AgeGate
}

VersionPolicy is how a refresh resolves the sources that ask for a version range. `ai-rulez update` sets Advance, AllowDowngrade and AcceptMovedTag; `lock` and `update` set ReleaseGate. The zero value keeps a pin that still satisfies its constraint and applies no minimum release age.

func (VersionPolicy) Advancing

func (v VersionPolicy) Advancing(kind, name string) bool

Advancing reports whether the refresh moves the source to its newest allowed tag.

type VersionSpec

type VersionSpec struct {
	// Constraint is the npm-style range ("" when the source uses a plain ref).
	Constraint        string
	TagPrefix         string
	IncludePrerelease bool
	// MinReleaseAge is the source's own minimum release age ("" defers to [lock]).
	MinReleaseAge string
}

VersionSpec is what a git source asks for when it uses a version constraint instead of a ref.

func (VersionSpec) Active

func (v VersionSpec) Active() bool

Active reports whether the source asks for a version range.

Source Files

Jump to

Keyboard shortcuts

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