Documentation
¶
Overview ¶
Package hooksync projects imported hooks into Claude Code settings. It is the hook tenant of pkg/project: scripts are copied into a gridctl-owned directory, and matcher groups are patched into the hooks key of ~/.claude/settings.json with recorded, region-level ownership. gridctl never executes a hook.
The ownership decision table follows pkg/wiring, with the identical-value adopt branch omitted. An unrecorded group is never claimed without --force. The settings write is an RFC 6902 patch on the parsed hujson tree, so bytes outside the patched elements, including comments, stay as they were found.
Index ¶
- Constants
- Variables
- func HasFailures(results []SyncResult) bool
- func KnownSlugs() []string
- func NeedsAttention(statuses []ProjectionStatus) bool
- func RemovalRefusal(name string, row UnsyncResult) string
- func SettingsUnparsable(detail string) bool
- type Entry
- type LockFile
- type Manager
- func (m *Manager) DetectedUnsupported() []UnsupportedClient
- func (m *Manager) HasProjections(ctx context.Context) (bool, error)
- func (m *Manager) Home() string
- func (m *Manager) LockPath() string
- func (m *Manager) Statuses(ctx context.Context) ([]ProjectionStatus, error)
- func (m *Manager) Sync(ctx context.Context, names []string, opts SyncOptions) ([]SyncResult, error)
- func (m *Manager) Unsync(ctx context.Context, names []string, opts UnsyncOptions) ([]UnsyncResult, error)
- type ProjectionStatus
- type SyncOptions
- type SyncResult
- type Target
- type UnenforcedHook
- type UnsupportedClient
- type UnsyncOptions
- type UnsyncResult
Constants ¶
const ( ActionCopied = "copied" ActionUpdated = project.ActionUpdated ActionUnchanged = project.ActionUnchanged ActionRemoved = "removed" ActionSkippedDrift = project.ActionSkippedDrift ActionSkippedForeign = "skipped-foreign" ActionSkippedPinDrift = "skipped-pin-drift" ActionSkippedUnmanaged = "skipped-unmanaged" ActionSkippedUnsupported = "skipped-unsupported" ActionWouldCopy = "would-copy" ActionWouldUpdate = project.ActionWouldUpdate ActionWouldRemove = "would-remove" ActionAlreadyGone = "already-gone" ActionError = project.ActionError StateInSync = project.StateInSync StateStale = project.StateStale StateDrifted = project.StateDrifted StateTargetMissing = project.StateTargetMissing // SettingsUnparsableDetail is the prefix used when a settings file is // not valid JSON or JSONC. Force does not override that case. SettingsUnparsableDetail = "settings file is not valid JSON or JSONC: " )
const ChannelMergeKey = "merge-key"
ChannelMergeKey is the only hook channel: gridctl owns matcher groups inside a shared settings file, never the file itself.
const RenderIdentity = "identity"
RenderIdentity is the only render class. The projected groups are the definition's groups with absolute script paths, not a dialect translation.
Variables ¶
var ( ErrUnknownClient = errors.New("unknown client") ErrUnknownHook = errors.New("unknown hook") ErrNotProjected = errors.New("hook is not projected") )
Sentinel errors callers branch on.
var ErrNewerLockVersion = project.ErrNewerLockVersion
ErrNewerLockVersion signals projection state written by a newer gridctl. Aliased from the engine so callers' errors.Is checks work.
Functions ¶
func HasFailures ¶
func HasFailures(results []SyncResult) bool
HasFailures reports whether any sync result needs the caller's attention. Every skipped or failed row counts. Dry-run would-* rows do not.
func KnownSlugs ¶
func KnownSlugs() []string
KnownSlugs lists target and unsupported slugs, for the unknown-client error.
func NeedsAttention ¶
func NeedsAttention(statuses []ProjectionStatus) bool
NeedsAttention reports whether any projection requires action.
func RemovalRefusal ¶
func RemovalRefusal(name string, row UnsyncResult) string
RemovalRefusal is the operator message for a blocked registry delete. It names the client and the unsync --force command.
func SettingsUnparsable ¶
SettingsUnparsable reports whether detail names an unparsable settings file.
Types ¶
type Entry ¶
type Entry struct {
// ConfigPath is the settings file. Path on the engine entry is the
// composite "<ConfigPath>#hooks/<name>" so several hooks share one file.
ConfigPath string
// ScriptDir is the absolute script copy.
ScriptDir string
// Hashes is the canonical hash history of the owned groups, newest last.
Hashes []string
// TreeHash is the hash of the script copy at sync time.
TreeHash string
// CreatedFile is true when gridctl created the settings file.
CreatedFile bool
Pack string
SyncedAt time.Time
}
Entry is one recorded (hook, client) ownership record.
type LockFile ¶
LockFile is the hook-kind view over the unified project lockfile, keyed hook name → client slug. The engine owns the on-disk schema.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns hook projections. All target paths resolve against home. Mutating operations serialize on mu and on the engine's cross-process lock.
func NewManager ¶
NewManager builds a Manager rooted at the user's home directory. It is for end-of-the-line CLI call sites only: any caller in pkg/ or internal/ that tests can reach must use NewManagerWithHome.
func NewManagerWithHome ¶
NewManagerWithHome builds a Manager rooted at an explicit home. Tests use this to stay isolated from $HOME.
func (*Manager) DetectedUnsupported ¶
func (m *Manager) DetectedUnsupported() []UnsupportedClient
DetectedUnsupported returns unsupported clients whose detect directory exists under the manager home. The CLI prints one stderr note per row when sync was not given --clients. Pack apply turns the same list into skipped-unsupported rows.
func (*Manager) HasProjections ¶
HasProjections reports whether any hook is currently projected.
func (*Manager) Statuses ¶
func (m *Manager) Statuses(ctx context.Context) ([]ProjectionStatus, error)
Statuses returns one row per recorded hook projection.
func (*Manager) Sync ¶
func (m *Manager) Sync(ctx context.Context, names []string, opts SyncOptions) ([]SyncResult, error)
Sync projects hooks into every available target, or into --clients. The settings file is read and written once per client, not once per hook.
func (*Manager) Unsync ¶
func (m *Manager) Unsync(ctx context.Context, names []string, opts UnsyncOptions) ([]UnsyncResult, error)
Unsync removes recorded groups whose hash is still one gridctl wrote, or any located groups when Force is set. Groups gridctl did not record are never deleted. The settings file is read and written once per client.
type ProjectionStatus ¶
type ProjectionStatus struct {
Hook string `json:"hook"`
Client string `json:"client"`
Channel string `json:"channel"`
Render string `json:"render"`
State string `json:"state"`
Target string `json:"target"`
ScriptDir string `json:"script_dir,omitempty"`
Pack string `json:"pack,omitempty"`
SyncedAt *time.Time `json:"synced_at,omitempty"`
Detail string `json:"detail,omitempty"`
Remediation string `json:"remediation,omitempty"`
}
ProjectionStatus is one recorded (hook, client) row.
type SyncOptions ¶
type SyncOptions struct {
Clients []string
Force bool
DryRun bool
// Pack tags recorded projections. Empty keeps any existing tag.
Pack string
}
SyncOptions configure a sync pass.
type SyncResult ¶
type SyncResult struct {
Hook string `json:"hook,omitempty"`
Client string `json:"client"`
Channel string `json:"channel,omitempty"`
Target string `json:"target,omitempty"`
Action string `json:"action"`
Detail string `json:"detail,omitempty"`
Remediation string `json:"remediation,omitempty"`
BackupPath string `json:"backup_path,omitempty"`
Error string `json:"error,omitempty"`
ScriptDir string `json:"script_dir,omitempty"`
}
SyncResult is one (hook, client) outcome.
func (SyncResult) ActionWasCopy ¶
func (r SyncResult) ActionWasCopy() bool
type Target ¶
type Target struct {
Slug string
Name string
// SettingsPath is a ~-template expanded against the Manager's home.
SettingsPath string
// DetectDirs mark the client as initialized on this machine.
DetectDirs []string
}
Target describes one client's settings file. Later dialect targets are additive: this release has Claude Code only.
func FindTarget ¶
FindTarget returns the projection target for slug.
type UnenforcedHook ¶
UnenforcedHook is one required hook that is not in-sync for a client.
func Unenforced ¶
func Unenforced(client string, required []string, statuses []ProjectionStatus) []UnenforcedHook
Unenforced reports required hooks that are not enforced on client. Reason is one of "not projected", "drifted", "stale", "pin drift", or "client has no hook target". State and exit codes are the caller's.
type UnsupportedClient ¶
UnsupportedClient is a client with a hook mechanism this release does not write. Sync reports it; it never creates the native file.
func FindUnsupported ¶
func FindUnsupported(slug string) (UnsupportedClient, bool)
FindUnsupported returns a known non-target client.
func UnsupportedClients ¶
func UnsupportedClients() []UnsupportedClient
UnsupportedClients returns clients that have a hook mechanism and no target in this release, in display order. Location is the native path named in skipped-unsupported rows and the stderr note.
type UnsyncOptions ¶
UnsyncOptions configure an unsync pass.
type UnsyncResult ¶
type UnsyncResult struct {
Hook string `json:"hook"`
Client string `json:"client"`
Target string `json:"target"`
Action string `json:"action"`
Detail string `json:"detail,omitempty"`
BackupPath string `json:"backup_path,omitempty"`
Error string `json:"error,omitempty"`
}
UnsyncResult describes one removal.
func BlocksRemoval ¶
func BlocksRemoval(results []UnsyncResult) (UnsyncResult, bool)
BlocksRemoval reports the first unsync row that did not retire the projection. removed and already-gone allow a registry delete to proceed. skipped-drift, skipped-unmanaged, error, and any other action do not.