approve

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Sep 13, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package approve manages the configuration that lets a coding agent call the seamark MCP tools without a permission prompt: Claude Code allow rules in .claude/settings.json and Codex per-tool approvals in .codex/config.toml. init writes both on --approve-tools; doctor and status read them. Both files are project configuration: evidence of intent, never a guarantee, because user-level and managed client policy still apply on top.

Index

Constants

View Source
const (
	ClientClaude = "claude"
	ClientCodex  = "codex"
)

Client names, as narrated.

View Source
const (
	StateUnreadable    = "unreadable"
	StateConflicting   = "conflicting"
	StateNotConfigured = "not configured"
	StatePartial       = "partial"
	StateCurrent       = "current"
)

Approval states, from nothing to done. Conflicting and unreadable need a person; partial and not configured need --approve-tools.

View Source
const ClaudeServer = "seamark"

ClaudeServer is the server name the rules use when .mcp.json does not register seamark: the name `claude mcp add seamark` gives it.

View Source
const ClaudeSettings = ".claude/settings.json"

ClaudeSettings is the Claude Code project file that carries the rules.

View Source
const CodexConfig = ".codex/config.toml"

CodexConfig is the project configuration Codex reads for trusted projects; the per-tool approvals and the server registration live in it. Repository-relative, slash-separated.

View Source
const CodexServer = "seamark"

CodexServer is the registration name init uses when none exists.

View Source
const MCPConfig = ".mcp.json"

MCPConfig is the Claude Code project file that registers MCP servers. The server name in it is the prefix of every tool rule, so the rules must be derived from it, never assumed.

Variables

View Source
var Tools = []string{"orient", "why", "change_set", "check", "expand"}

Tools is the seamark MCP tool surface, in definition order. The list lives here rather than in internal/mcp because status imports this package and mcp imports status; a test in internal/mcp pins the two lists to each other.

Functions

func AllowSet

func AllowSet(settings map[string]any) map[string]bool

AllowSet returns the string entries of permissions.allow, reading the settings without creating anything.

func ApplyCodex

func ApplyCodex(w io.Writer, root string, p *CodexPlan, printOnly bool) error

ApplyCodex writes the planned TOML and narrates in init's vocabulary. Every existing byte, comments included, stays where it is: the block is appended, and the two registration keys that complete a header-only table are inserted under that header. The path is checked for links again right before the write, because the plan may be older than the tree.

func ClaudeRules

func ClaudeRules() ([]string, error)

ClaudeRules returns the rules for the conventional server name. The benchmark and the tests use it; init, doctor, and status derive the name from .mcp.json through ClaudeRulesFor.

func ClaudeRulesFor

func ClaudeRulesFor(server string) ([]string, error)

ClaudeRulesFor returns the allow rules --approve-tools merges into .claude/settings.json for one server name: one exact rule per MCP tool and one exact Skill rule per shipped skill. Exact names, never Skill(seamark-*): a wildcard would pre-approve a skill that does not exist yet. The rules do not depend on whether the skills are installed, so an MCP-only setup can approve the tools alone; the benchmark's MCP-only arm needs exactly that.

func KeptSuffix

func KeptSuffix(conflicts []string) string

KeptSuffix renders the conflicts for an init line, or "" when there are none. init's Claude and Codex lines share it, so the two read alike.

func ServerRule

func ServerRule(server string) string

ServerRule spells the Claude Code rule that covers every tool of one server. Claude Code documents it beside the per-tool form.

func Summary

func Summary(states []ClientApproval) string

Summary renders the one-line view status prints, for example "claude 8/8 rules · codex not registered". A partial configuration names the corrective command once at the end.

func ToolRule

func ToolRule(server, tool string) string

ToolRule spells the Claude Code permission rule for one MCP tool.

Types

type ClaudePlan

type ClaudePlan struct {
	// Server is the name the tool rules are spelled with.
	Server string
	// Rules is the complete rule set for the server.
	Rules []string
	// Approved lists the rules permissions.allow covers; Missing the
	// rules the merge appends; Conflicts the explicit deny or ask
	// entries that keep a rule from working, described.
	Approved  []string
	Missing   []string
	Conflicts []string
}

ClaudePlan is the outcome of inspecting the permissions in .claude/settings.json for one server name: what is approved, what --approve-tools adds, and what it leaves alone.

func PlanClaude

func PlanClaude(settings map[string]any, server string) (*ClaudePlan, error)

PlanClaude classifies every rule against the three permission lists. Claude Code applies deny, then ask, then allow. A rule in deny or ask is therefore a conflict however allow reads. Appending an allow rule for it would change nothing, and counting it as approved would hide a prompt. The server-wide rule counts for every tool of the server, in each list. An entry that covers several tools is reported once.

func (*ClaudePlan) Conflicting

func (p *ClaudePlan) Conflicting() int

Conflicting counts the rules the conflicts keep from working. It is not len(Conflicts): one server-wide deny entry covers five tool rules.

type ClientApproval

type ClientApproval struct {
	Client string `json:"client"`
	// Path is the repository-relative configuration file.
	Path string `json:"path"`
	// Registered is the server name that runs `seamark mcp`: the
	// .codex/config.toml table for Codex, the .mcp.json entry for Claude
	// Code. Empty when nothing is registered.
	Registered string `json:"registered,omitempty"`
	// Approved counts the rules (Claude Code) or tools (Codex) present;
	// Total is what a complete configuration holds.
	Approved int `json:"approved"`
	Total    int `json:"total"`
	// Conflicts lists explicit settings seamark leaves alone, such as a
	// tool set to prompt, a denied rule, or a server name in use.
	Conflicts []string `json:"conflicts,omitempty"`
	// Err reports a file that cannot be read or parsed.
	Err string `json:"error,omitempty"`
}

ClientApproval summarizes one client's approval configuration for init, doctor, and status.

func Inspect

func Inspect(root string) []ClientApproval

Inspect reports both clients regardless of detection, so a partial configuration in a directory auto would skip stays visible. Errors are recorded on the client, never returned: status must describe a broken setup, not fail on it.

func (ClientApproval) Describe

func (c ClientApproval) Describe() string

Describe renders one client in a few words, for example "claude 8/8 rules" or "codex registered as \"seamark\", 5/5 tools approved". init, doctor, and status all print it. A Claude Code server under a name other than the conventional one is named, because the rules are spelled with it. "not configured" is reserved for a client with no registration and no rules, so the words agree with State: a registered client with no rules is partial and reads "claude 0/8 rules", the same way Codex reads "0/5 tools approved".

func (ClientApproval) State

func (c ClientApproval) State() string

State classifies the record by one rule for both clients. A client with no registration and no approvals is not configured, which is information. A registered client with fewer approvals than tools is partial, zero included: a registration alone approves nothing, so every call prompts and the re-run hint is due. Codex approvals without a registration are partial too, because the re-run adds the missing registration.

type CodexPlan

type CodexPlan struct {
	// Exists reports whether the file is present.
	Exists bool
	// Server is the registration the approvals attach to; empty when a
	// conflict prevents both reuse and registration.
	Server string
	// Registered is true when Server names a registration the file
	// already holds; Register is true when the block adds one. Both are
	// false when the file cannot be extended.
	Registered bool
	Register   bool
	// Missing lists the tools the block approves; Approved the tools
	// already approved, by their own entry or by an approve default.
	Missing  []string
	Approved []string
	// Conflicts lists explicit settings left alone, described.
	Conflicts []string
	// contains filtered or unexported fields
}

CodexPlan is the outcome of inspecting .codex/config.toml before any write: what exists, what the appended block adds, and what seamark leaves alone.

func PlanCodex

func PlanCodex(root string) (*CodexPlan, error)

PlanCodex inspects .codex/config.toml without writing. It rejects a symlinked path, malformed TOML, and wrong-typed or invalid values on the seamark server, because init must fail before its first write and Codex would refuse the file. A conflicting registration or an explicit restrictive setting, a disabled server or a server-wide approval default included, is reported in the plan and left alone, because a person put it there. The appended block is validated by parsing the would-be file, and inline tables are detected from the decoded keys, so a layout that appending cannot extend is reported instead of written.

type Registration

type Registration struct {
	// Exists reports whether the file is present.
	Exists bool
	// Server is the name whose command is seamark; empty when none.
	Server string
}

Registration describes what .mcp.json says about seamark.

func ClaudeRegistration

func ClaudeRegistration(root string) (Registration, error)

ClaudeRegistration reads .mcp.json and finds the seamark server by the same basename rule doctor and hooks use: exact "seamark", tolerating the Windows suffix. Several matches prefer the conventional name, then the first in name order, so repeated runs agree. A missing file is not an error; an unparseable one is, because a rule prefix guessed from a broken file would be reported as current while every call prompts.

func (Registration) ServerName

func (r Registration) ServerName() string

ServerName returns the name the tool rules must use: the registered one, or the conventional name when nothing is registered yet.

Jump to

Keyboard shortcuts

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