Documentation
¶
Index ¶
- Constants
- func BoardTransition(addr, token string, req BoardTransitionRequest) error
- func ClassifyMarker(body string) (BoardStage, BoardState, bool)
- func ConnectedPath() string
- func GenerateToken() (string, error)
- func GetHookSnapshot(addr, token string) (map[string]hookevents.SessionSnapshot, error)
- func GetLogMode(addr, token string) (string, error)
- func GetToolStats(addr, token string) (*stats.ToolStats, error)
- func GetTrackerDiagnose(addr, token string) ([]tracker.TrackerStatus, error)
- func InfoPath() string
- func IsProcessAlive(pid int) bool
- func IsReviewComplete(body string) bool
- func LoadOrCreateToken() (string, error)
- func LogPath() string
- func ParseEngineeringKeysFromHandoff(body string) []string
- func ParseHookEventArgs(args []string) hookevents.Event
- func ParsePRFromHandoff(body string) string
- func PidPath() string
- func QueryAudit(addr, token string, filterArgs []string) ([]audit.Event, error)
- func ReadAlivePid() (int, bool)
- func ReadConnected(path string) []int
- func RemoveConnected(path string)
- func RemoveInfo()
- func RemovePidFile()
- func RunAgentCleanup(ctx context.Context, store *HookEventStore, cleaner AgentCleaner, ...)
- func RunAgentZombieSweep(ctx context.Context, sweeper AgentZombieSweeper, logger zerolog.Logger)
- func RunBoardFailureWatch(ctx context.Context, store *HookEventStore, ...)
- func RunRemote(addr, token string, args []string, version string) (int, error)
- func RunRemoteCapture(addr, token string, args []string) ([]byte, error)
- func SendConfirmDecision(addr, token, id string, approved bool) error
- func SetLogMode(addr, token, mode string) (string, error)
- func Subscribe(addr, token string) (<-chan SubscribeEvent, func(), error)
- func TokenPath() string
- func WriteConnected(path string, pids []int) error
- func WriteInfo(info DaemonInfo) error
- func WritePidFile(pid int) error
- type AgentCleaner
- type AgentInfo
- type AgentLauncher
- type AgentZombieSweeper
- type BoardCard
- type BoardStage
- type BoardState
- type BoardTransitionDeps
- type BoardTransitionRequest
- type BrowserOpener
- type ConnectedTracker
- type DaemonInfo
- type HookEventStore
- func (s *HookEventStore) Append(evt hookevents.Event)
- func (s *HookEventStore) EventsSince(since uint64) ([]hookevents.Event, uint64)
- func (s *HookEventStore) RecentEvents() []hookevents.Event
- func (s *HookEventStore) Snapshot() map[string]hookevents.SessionSnapshot
- func (s *HookEventStore) Subscribe() chan struct{}
- func (s *HookEventStore) Unsubscribe(ch chan struct{})
- type NetworkEvent
- type NetworkEventStore
- type PRPublisher
- type PRRequest
- type PendingConfirm
- type PendingConfirmStore
- func (s *PendingConfirmStore) Add(pc *PendingConfirmation)
- func (s *PendingConfirmStore) Cleanup(maxAge time.Duration)
- func (s *PendingConfirmStore) Len() int
- func (s *PendingConfirmStore) Resolve(id string, approved bool, approverPID int) error
- func (s *PendingConfirmStore) ResolveTimeout(id string)
- func (s *PendingConfirmStore) Snapshot() []PendingConfirm
- type PendingConfirmation
- type ProjectEntry
- type ProjectInfo
- type ProjectRegistry
- type Request
- type Response
- type Server
- type SubscribeEvent
- type TrackerIssuesResult
Constants ¶
const ( PlanningStartedHeader = "[human:planning-started]" PlanReadyHeader = "[human:plan-ready]" PlanningFailedHeader = "[human:planning-failed]" ImplementationStartedHeader = "[human:implementation-started]" ImplementationFailedHeader = "[human:implementation-failed]" ReviewStartedHeader = "[human:review-started]" ReviewFailedHeader = "[human:review-failed]" PRStartedHeader = "[human:pr-started]" PRPushedHeader = "[human:pr-pushed]" PRFailedHeader = "[human:pr-failed]" )
Board marker headers. These mirror the existing review-handoff headers in review_handoff.go and follow the same `strings.HasPrefix(trimmed, header)` contract: a comment that merely quotes a header mid-body is NOT a marker.
ReadyForReviewHeader is reused as the implementation done-marker and ReviewCompleteHeader as the verification done-marker; both are declared in review_handoff.go and intentionally NOT redeclared here.
const ( // DefaultPort is the well-known daemon listening port. DefaultPort = 19285 // DefaultChromePort is the well-known Chrome proxy port. DefaultChromePort = 19286 // DefaultProxyPort is the well-known HTTPS proxy port. DefaultProxyPort = 19287 // DockerHost is the hostname Docker provides for reaching the host machine // from inside a container. Enabled by --add-host=host.docker.internal:host-gateway. DockerHost = "host.docker.internal" )
const ReadyForReviewHeader = "[human:ready-for-review]"
ReadyForReviewHeader is the single-line magic header that identifies a review handoff in a PM-ticket comment. See cli/CLAUDE.md "Review handoff".
const ReviewCompleteHeader = "[human:review-complete]"
ReviewCompleteHeader is the matching follow-up header posted after the reviewer agent finishes. Presence of a *newer* review-complete comment clears the ready-for-review flag, so the TUI stops showing (R) once a review has actually landed.
Variables ¶
This section is empty.
Functions ¶
func BoardTransition ¶ added in v0.20.0
func BoardTransition(addr, token string, req BoardTransitionRequest) error
BoardTransition asks the daemon to advance a card one pipeline stage. The request is sent as a single JSON arg so multi-word PM titles survive arg splitting on the daemon side.
func ClassifyMarker ¶ added in v0.20.0
func ClassifyMarker(body string) (BoardStage, BoardState, bool)
ClassifyMarker reports the stage and state a comment body represents and whether it is a recognized board marker at all. A body is only a marker when it STARTS with a known header (after trimming), so a quoted header in the middle of a discussion comment does not register. Pure: no I/O.
func ConnectedPath ¶
func ConnectedPath() string
ConnectedPath returns the default path for the connected PIDs file.
func GenerateToken ¶
GenerateToken returns a cryptographically random 32-byte hex string.
func GetHookSnapshot ¶
func GetHookSnapshot(addr, token string) (map[string]hookevents.SessionSnapshot, error)
GetHookSnapshot fetches the current per-session hook state from the daemon.
func GetLogMode ¶
GetLogMode fetches the current traffic log mode from the daemon.
func GetToolStats ¶
GetToolStats fetches pre-aggregated tool call statistics from the daemon.
func GetTrackerDiagnose ¶
func GetTrackerDiagnose(addr, token string) ([]tracker.TrackerStatus, error)
GetTrackerDiagnose fetches tracker credential status from the daemon.
func InfoPath ¶
func InfoPath() string
InfoPath returns the default path for the daemon info file (~/.human/daemon.json).
func IsProcessAlive ¶
IsProcessAlive checks whether a process with the given PID is still running.
func IsReviewComplete ¶
IsReviewComplete reports whether the comment body is a review-complete follow-up, which supersedes any earlier handoff for the same engineering keys.
func LoadOrCreateToken ¶
LoadOrCreateToken reads the token from disk, or generates and persists a new one.
func LogPath ¶
func LogPath() string
LogPath returns the path to the daemon log file (~/.human/daemon.log).
func ParseEngineeringKeysFromHandoff ¶
ParseEngineeringKeysFromHandoff extracts the engineering ticket keys listed on the `engineering:` line of a [human:ready-for-review] comment body. Returns nil if the body is not a handoff block or has no engineering line.
The comment body must START with ReadyForReviewHeader so a comment that merely quotes the header (e.g. in a discussion) does not trigger a handoff.
func ParseHookEventArgs ¶
func ParseHookEventArgs(args []string) hookevents.Event
ParseHookEventArgs converts daemon request args into a hook event. Expected args: [event, session_id, cwd, notification_type, tool_name, error_type, agent_name].
Every field is length-capped and Cwd must be absolute — both as defence against abusive clients that could otherwise poison the in-memory hook store or write relative paths that collide with registered project directories.
func ParsePRFromHandoff ¶ added in v0.20.0
ParsePRFromHandoff extracts the pull-request URL from the optional `pr:` line of a [human:ready-for-review] comment body. Returns "" when the body is not a handoff block or carries no pr: line (the line is optional — handoffs from flows that only push a branch omit it).
func PidPath ¶
func PidPath() string
PidPath returns the path to the daemon PID file (~/.human/daemon.pid).
func QueryAudit ¶ added in v0.20.0
QueryAudit reads audit events from the daemon (which owns the audit DB), forwarding the pre-parsed filter flags. filterArgs is the slice of --since/--until/--subject/--tracker/--limit tokens.
func ReadAlivePid ¶
ReadAlivePid reads the PID file and checks if the process is alive. Returns (0, false) if no PID file exists or the process is dead.
func ReadConnected ¶
ReadConnected reads connected PIDs from path. Returns nil on any error.
func RemoveConnected ¶
func RemoveConnected(path string)
RemoveConnected removes the connected PIDs file (best-effort).
func RunAgentCleanup ¶
func RunAgentCleanup(ctx context.Context, store *HookEventStore, cleaner AgentCleaner, logger zerolog.Logger)
RunAgentCleanup watches for SessionEnd hook events from devcontainer agents and automatically stops the container and removes the worktree.
func RunAgentZombieSweep ¶
func RunAgentZombieSweep(ctx context.Context, sweeper AgentZombieSweeper, logger zerolog.Logger)
RunAgentZombieSweep periodically checks for agent containers that are still running but have no Claude process. This catches cases where Claude failed to start, crashed without firing hook events, or the user killed the tmux pane.
func RunBoardFailureWatch ¶ added in v0.20.0
func RunBoardFailureWatch(ctx context.Context, store *HookEventStore, commenterFor func() (tracker.Commenter, error), logger zerolog.Logger)
RunBoardFailureWatch watches for SessionEnd-style hook events from board agents and posts the stage's *-failed marker when an agent exits WITHOUT having posted its stage's done-marker. This closes the gap where an agent dies (or is killed) mid-stage: the board would otherwise show a stuck spinner forever. It mirrors RunAgentCleanup's subscribe loop.
commenterFor resolves the PM-role commenter lazily (per event) so the watcher holds no tracker handle across its lifetime; the PM commenter MUST be resolved by role, never by key prefix (both trackers may share a name).
func RunRemote ¶
RunRemote connects to the daemon at addr, sends the CLI args, and returns the exit code. Stdout and stderr are written to os.Stdout and os.Stderr.
func RunRemoteCapture ¶
RunRemoteCapture connects to the daemon and runs args, returning stdout as bytes instead of printing to os.Stdout.
func SendConfirmDecision ¶
SendConfirmDecision sends a confirmation decision for a pending destructive operation.
func SetLogMode ¶
SetLogMode sets the traffic log mode on the daemon. Returns the new mode.
func Subscribe ¶
func Subscribe(addr, token string) (<-chan SubscribeEvent, func(), error)
Subscribe opens a persistent connection to the daemon's subscribe endpoint. It returns a channel that receives a signal each time the daemon's state changes, and a cleanup function that closes the connection. The channel is closed when the connection drops or cleanup is called.
func TokenPath ¶
func TokenPath() string
TokenPath returns the default path for the daemon token file.
func WriteConnected ¶
WriteConnected atomically writes the connected PIDs to path. On rename failure the temporary file is removed so a crashed daemon does not leave orphan .tmp files that survive across restarts when the process is killed with SIGKILL.
func WriteInfo ¶
func WriteInfo(info DaemonInfo) error
WriteInfo writes the daemon info as JSON to InfoPath with restricted permissions.
func WritePidFile ¶
WritePidFile writes the given PID to the PID file.
Types ¶
type AgentCleaner ¶
type AgentCleaner interface {
DeleteAgent(ctx context.Context, name string) error
// DecommissionAgent removes the agent from the list immediately and
// returns the container ID for background teardown. This makes
// "human agent list" responsive while the slow container stop happens
// asynchronously.
DecommissionAgent(name string) (containerID string, err error)
// StopContainer stops and removes a container by ID.
StopContainer(ctx context.Context, containerID string) error
}
AgentCleaner stops and removes an agent by name.
type AgentLauncher ¶ added in v0.20.0
type AgentLauncher interface {
Launch(ctx context.Context, name, prompt, workspace, configDir string) error
}
AgentLauncher launches a containerized agent for a board stage. It is an interface so the transition engine is testable without Docker.
type AgentZombieSweeper ¶
type AgentZombieSweeper interface {
RunningAgents() ([]AgentInfo, error)
IsProcessRunning(ctx context.Context, containerID string, process string) (bool, error)
DeleteAgent(ctx context.Context, name string) error
}
AgentZombieSweeper checks for orphaned agent containers whose main process has exited but the container is still running.
type BoardCard ¶ added in v0.20.0
type BoardCard struct {
Stage BoardStage `json:"stage"`
State BoardState `json:"state"`
EngineeringKey string `json:"engineering_key,omitempty"`
Branch string `json:"branch,omitempty"`
PRURL string `json:"pr_url,omitempty"`
Error string `json:"error,omitempty"`
}
BoardCard is the derived per-PM placement on the pipeline board. It is the single source of truth shared on the wire with the GUI and TUI, so neither re-derives from raw comments.
func DeriveBoardCard ¶ added in v0.20.0
DeriveBoardCard computes a PM ticket's board placement from its comment thread and tracker status. The rule: the furthest stage carrying ANY marker wins; within that stage the latest marker (by Created) decides running/done/failed. A ticket with no markers sits in Backlog while open and is Hidden once closed/done (those never entered the pipeline). Pure: no I/O.
type BoardStage ¶ added in v0.20.0
type BoardStage string
BoardStage is one of the five pipeline columns (plus a synthetic "hidden" stage for closed PM tickets that never entered the pipeline). The drag-board GUI renders cards into these columns; the daemon derives the stage from the [human:…] comment markers a PM ticket carries.
const ( BoardBacklog BoardStage = "backlog" BoardPlanning BoardStage = "planning" BoardImplementation BoardStage = "implementation" BoardVerification BoardStage = "verification" BoardDoneStage BoardStage = "done" BoardHidden BoardStage = "hidden" )
type BoardState ¶ added in v0.20.0
type BoardState string
BoardState is the within-stage status of a card: empty for an idle card sitting at the head of a stage, running while an agent works the stage, done once the stage's success marker lands, failed on an error marker.
const ( BoardIdle BoardState = "" BoardRunning BoardState = "running" BoardDone BoardState = "done" BoardFailed BoardState = "failed" )
type BoardTransitionDeps ¶ added in v0.20.0
type BoardTransitionDeps struct {
Commenter tracker.Commenter
Launcher AgentLauncher
Publisher PRPublisher
WorkspaceDir string
ConfigDir string
}
BoardTransitionDeps wires the transition engine's collaborators.
func (BoardTransitionDeps) ApplyTransition ¶ added in v0.20.0
func (d BoardTransitionDeps) ApplyTransition(ctx context.Context, req BoardTransitionRequest) error
ApplyTransition advances a card from its current stage to the requested next stage. The daemon re-loads live comments and re-derives the card here because the UI gate is advisory only — the daemon is the authority on whether a forward move is allowed (forward-only, single-step, gated on the prior stage's completion). All errors carry details for the client.
type BoardTransitionRequest ¶ added in v0.20.0
type BoardTransitionRequest struct {
PMKey string `json:"pm_key"`
PMTitle string `json:"pm_title"`
From BoardStage `json:"from"`
To BoardStage `json:"to"`
}
BoardTransitionRequest is the wire request for advancing a card one stage. PMTitle is carried from the card so the Done stage can title the PR without a second tracker fetch.
type BrowserOpener ¶
BrowserOpener opens a URL in the browser. Extracted for testability.
type ConnectedTracker ¶
type ConnectedTracker struct {
// contains filtered or unexported fields
}
ConnectedTracker maintains a thread-safe set of recently-seen client PIDs. Each PID has a last-seen timestamp; Prune removes entries older than a TTL.
func NewConnectedTracker ¶
func NewConnectedTracker() *ConnectedTracker
NewConnectedTracker creates an empty tracker.
func (*ConnectedTracker) PIDs ¶
func (t *ConnectedTracker) PIDs() []int
PIDs returns a sorted snapshot of currently tracked PIDs.
func (*ConnectedTracker) Prune ¶
func (t *ConnectedTracker) Prune(ttl time.Duration)
Prune removes PIDs not seen within ttl.
func (*ConnectedTracker) Touch ¶
func (t *ConnectedTracker) Touch(pid int)
Touch records or refreshes a PID with the current time.
type DaemonInfo ¶
type DaemonInfo struct {
Addr string `json:"addr"`
ChromeAddr string `json:"chrome_addr,omitempty"`
ProxyAddr string `json:"proxy_addr,omitempty"`
Token string `json:"token,omitempty"`
PID int `json:"pid,omitempty"`
// Version carries the daemon binary's build version so clients can warn
// about skew between the running daemon and the CLI binary.
// omitempty preserves backward-compatibility with daemon.json files
// written by older builds that do not emit this field.
Version string `json:"version,omitempty"`
Projects []ProjectInfo `json:"projects,omitempty"`
}
DaemonInfo holds the runtime details of a running daemon instance.
func ReadInfo ¶
func ReadInfo() (DaemonInfo, error)
ReadInfo reads and unmarshals the daemon info from InfoPath.
func (DaemonInfo) IsAlive ¶
func (d DaemonInfo) IsAlive() bool
IsAlive checks whether the daemon process identified by PID is still running.
func (DaemonInfo) IsReachable ¶
func (d DaemonInfo) IsReachable() bool
IsReachable checks whether the daemon is accepting TCP connections at its advertised address. This works across process namespaces (e.g. host ↔ devcontainer) where PID-based checks fail.
type HookEventStore ¶
type HookEventStore struct {
// contains filtered or unexported fields
}
HookEventStore is a thread-safe ring buffer of recent hook events. It stores raw events and can derive per-session snapshots on demand. Subscribers are notified (non-blocking) whenever a new event is appended.
func NewHookEventStore ¶
func NewHookEventStore() *HookEventStore
NewHookEventStore creates an empty store.
func (*HookEventStore) Append ¶
func (s *HookEventStore) Append(evt hookevents.Event)
Append adds a hook event. Before appending, events for the same session beyond maxHookEventsPerSession are dropped so one chatty client cannot push other sessions out of the shared buffer. The aggregate cap then drops the oldest events across all sessions.
func (*HookEventStore) EventsSince ¶
func (s *HookEventStore) EventsSince(since uint64) ([]hookevents.Event, uint64)
EventsSince returns the events appended after the given sequence, plus the current high-water sequence to pass on the next call. Sequences are monotonic and independent of the ring's length, so a subscriber keeps receiving new events even after the ring saturates and stops growing — the failure mode of tracking deltas by slice length. Pass 0 for the first call.
func (*HookEventStore) RecentEvents ¶
func (s *HookEventStore) RecentEvents() []hookevents.Event
RecentEvents returns a copy of all stored events.
func (*HookEventStore) Snapshot ¶
func (s *HookEventStore) Snapshot() map[string]hookevents.SessionSnapshot
Snapshot returns the current per-session state derived from all stored events.
func (*HookEventStore) Subscribe ¶
func (s *HookEventStore) Subscribe() chan struct{}
Subscribe returns a channel that receives a signal whenever a new event is appended. The channel has a buffer of 1 so a single pending notification is coalesced. Call Unsubscribe to clean up.
func (*HookEventStore) Unsubscribe ¶
func (s *HookEventStore) Unsubscribe(ch chan struct{})
Unsubscribe removes a previously registered channel from the subscriber list. The channel is not closed — subscribers must stop reading from it after calling Unsubscribe and let it be garbage collected. This avoids coordinating with any concurrent Append on a removed channel.
type NetworkEvent ¶
type NetworkEvent struct {
Source string `json:"source"` // "proxy" | "oauth" | "fail"
Status string `json:"status"` // "forward" | "intercept" | "block" | "no-sni" | "parse-fail" | "dial-fail" | "callback"
Host string `json:"host"` // may be empty for pre-SNI failures
Count int `json:"count"` // >=1
LastSeen time.Time `json:"last_seen"`
}
NetworkEvent is a single ambient network activity row as rendered by the TUI activity panel. Consecutive events with the same Host and Source are collapsed into one row with an incrementing Count and a refreshed LastSeen timestamp.
func GetNetworkEvents ¶
func GetNetworkEvents(addr, token string) ([]NetworkEvent, error)
GetNetworkEvents fetches the current ambient network activity buffer from the daemon. Returns a nil slice (not a nil error) when the daemon replies with an empty list so the TUI can collapse the panel.
type NetworkEventStore ¶
type NetworkEventStore struct {
// contains filtered or unexported fields
}
NetworkEventStore is a thread-safe ring buffer of recent network events with consecutive-host deduplication. It models HookEventStore but collapses bursts at write time so the panel stays calm under sustained repeats.
func NewNetworkEventStore ¶
func NewNetworkEventStore() *NetworkEventStore
NewNetworkEventStore creates an empty store using time.Now as its clock. Tests can override the clock via NewNetworkEventStoreWithClock.
func NewNetworkEventStoreWithClock ¶
func NewNetworkEventStoreWithClock(nowFn func() time.Time) *NetworkEventStore
NewNetworkEventStoreWithClock is the test constructor that injects a deterministic clock so dedup timestamps are reproducible.
func (*NetworkEventStore) Emit ¶
func (s *NetworkEventStore) Emit(source, status, host string)
Emit satisfies proxy.NetworkEventEmitter. Consecutive events with the same (source, host) pair are collapsed into the tail row: Count is incremented and LastSeen is refreshed. A different host or source starts a new row, even if the host was seen earlier in the buffer. Collapsing at write time keeps memory flat under sustained bursts so the panel stays calm under noise.
func (*NetworkEventStore) Snapshot ¶
func (s *NetworkEventStore) Snapshot() []NetworkEvent
Snapshot returns a copy of all current rows in insertion order (oldest first). Callers that want newest-first should reverse the slice; the store keeps events in insertion order so testing and JSON serialization remain predictable.
type PRPublisher ¶ added in v0.20.0
type PRPublisher interface {
PushAndCreatePR(ctx context.Context, req PRRequest) (prURL string, err error)
}
PRPublisher pushes the recorded branch and opens a pull request. Injected so the Done stage is testable without git/forge access.
type PRRequest ¶ added in v0.20.0
PRRequest carries everything needed to push a branch and open its PR.
type PendingConfirm ¶
type PendingConfirm struct {
ID string `json:"id"`
Operation string `json:"operation"` // "DeleteIssue", "EditIssue"
Tracker string `json:"tracker"` // tracker kind, e.g. "jira", "linear"
Key string `json:"key"` // issue key, e.g. "KAN-1"
Prompt string `json:"prompt"`
CreatedAt string `json:"created_at"`
ClientPID int `json:"client_pid"` // PID of the Claude instance that triggered the operation
}
PendingConfirm is the wire type for a single pending destructive operation awaiting user confirmation via the TUI.
func GetPendingConfirms ¶
func GetPendingConfirms(addr, token string) ([]PendingConfirm, error)
GetPendingConfirms fetches pending destructive operation confirmations from the daemon.
type PendingConfirmStore ¶
type PendingConfirmStore struct {
// contains filtered or unexported fields
}
PendingConfirmStore is a thread-safe store for destructive operations awaiting user confirmation. The daemon adds entries when it intercepts destructive commands; the TUI polls the snapshot and resolves them.
func NewPendingConfirmStore ¶
func NewPendingConfirmStore() *PendingConfirmStore
NewPendingConfirmStore creates an empty store.
func (*PendingConfirmStore) Add ¶
func (s *PendingConfirmStore) Add(pc *PendingConfirmation)
Add stores a pending confirmation. The caller should block on pc.Decision after calling Add.
func (*PendingConfirmStore) Cleanup ¶
func (s *PendingConfirmStore) Cleanup(maxAge time.Duration)
Cleanup rejects and removes all pending confirmations older than maxAge.
func (*PendingConfirmStore) Len ¶
func (s *PendingConfirmStore) Len() int
Len returns the number of pending confirmations.
func (*PendingConfirmStore) Resolve ¶
func (s *PendingConfirmStore) Resolve(id string, approved bool, approverPID int) error
Resolve sends the decision to the waiting goroutine and removes the entry. approverPID is the PID of the client resolving the confirmation and must be a positive integer.
The approverPID != requester PID check below is only a best-effort sanity guard, NOT an authorization boundary: ClientPID is supplied by the client and the requester's PID is typically resolved inside the agent's container namespace while the approver's is on the host, so the two are not comparable as a trust signal. Actual authorization is the daemon token required to reach this endpoint at all — do not rely on the PID check for security.
Resolve is for client-initiated decisions. Internal lifecycle events (timeouts, encode failures) must use ResolveTimeout instead.
func (*PendingConfirmStore) ResolveTimeout ¶
func (s *PendingConfirmStore) ResolveTimeout(id string)
ResolveTimeout removes a pending confirmation without a client approver. It is used by internal lifecycle events (request timeouts, response-write failures) that need to unblock the waiting goroutine. The decision is always "not approved".
Returns nil even when the id is unknown — the caller is typically running in a deferred cleanup path where the entry may have already been resolved by another lifecycle event, and that is not a failure.
func (*PendingConfirmStore) Snapshot ¶
func (s *PendingConfirmStore) Snapshot() []PendingConfirm
Snapshot returns all pending confirmations as wire types for the TUI.
type PendingConfirmation ¶
type PendingConfirmation struct {
ID string
Operation string // "DeleteIssue", "EditIssue"
Tracker string // tracker kind, e.g. "jira"
Key string // issue key, e.g. "KAN-1"
Prompt string // human-readable, e.g. "Delete KAN-1?"
ClientPID int // PID of the Claude instance that triggered the operation
CreatedAt time.Time
Decision chan bool // the blocked goroutine waits on this; true = approved
}
PendingConfirmation represents a destructive operation that is blocked waiting for user confirmation via the TUI.
type ProjectEntry ¶
type ProjectEntry struct {
Name string // from .humanconfig project: field, or directory basename
Dir string // absolute path to project directory
}
ProjectEntry holds the loaded config context for one registered project directory.
func (ProjectEntry) EnvLookup ¶
func (p ProjectEntry) EnvLookup() config.EnvLookup
EnvLookup returns a per-project scoped environment variable lookup function. It implements a 4-level precedence chain for each key:
- HUMAN_{PROJECT}_{KEY} — per-project override (e.g. HUMAN_INFRA_GITHUB_WORK_TOKEN)
- {KEY} via os.LookupEnv — global fallback (e.g. GITHUB_WORK_TOKEN)
The caller (ApplyEnvOverrides) constructs keys like PREFIX_SUFFIX and PREFIX_INSTANCE_SUFFIX. This lookup prepends HUMAN_{PROJECT}_ and checks that first, falling back to os.LookupEnv for the original key.
type ProjectInfo ¶
ProjectInfo describes a registered project in a running daemon.
type ProjectRegistry ¶
type ProjectRegistry struct {
// contains filtered or unexported fields
}
ProjectRegistry maps project directories to their config entries. It is created at daemon startup and is read-only thereafter (no mutex needed).
func NewProjectRegistry ¶
func NewProjectRegistry(dirs []string) (*ProjectRegistry, error)
NewProjectRegistry creates a registry from a list of project directories. Each directory must exist and contain a readable .humanconfig. If .humanconfig lacks a project: field, the directory basename is used as the name.
func (*ProjectRegistry) Entries ¶
func (r *ProjectRegistry) Entries() []ProjectEntry
Entries returns all registered project entries.
func (*ProjectRegistry) Resolve ¶
func (r *ProjectRegistry) Resolve(cwd string) (ProjectEntry, bool)
Resolve finds the ProjectEntry whose Dir is a prefix of the given cwd. Returns (entry, true) on match, (zero, false) if no match. When multiple entries match (nested dirs), the longest prefix wins because entries are sorted by path length descending.
func (*ProjectRegistry) Single ¶
func (r *ProjectRegistry) Single() bool
Single returns true if there is exactly one registered project (backward compat mode).
type Request ¶
type Request struct {
Version string `json:"version"`
Token string `json:"token"`
Args []string `json:"args"`
Env map[string]string `json:"env,omitempty"`
ClientPID int `json:"client_pid,omitempty"` // parent PID (Claude process) for connection tracking
Cwd string `json:"cwd,omitempty"` // client working directory for project routing
}
Request is sent from the client to the daemon (one JSON line per connection).
type Response ¶
type Response struct {
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
ExitCode int `json:"exit_code"`
AwaitCallback bool `json:"await_callback,omitempty"`
Callback string `json:"callback,omitempty"`
AwaitConfirm bool `json:"await_confirm,omitempty"` // line 1: daemon paused, awaiting TUI confirmation
ConfirmID string `json:"confirm_id,omitempty"` // unique identifier for the pending operation
ConfirmPrompt string `json:"confirm_prompt,omitempty"` // human-readable prompt, e.g. "Delete JIRA-123?"
}
Response is sent from the daemon back to the client (one or more JSON lines per connection).
type Server ¶
type Server struct {
Addr string
Token string
SafeMode bool
CmdFactory func() *cobra.Command
Opener BrowserOpener // used for OAuth relay; defaults to browser.DefaultOpener
Logger zerolog.Logger
ConnectedPIDs *ConnectedTracker // tracks client PIDs that have pinged; nil disables tracking
HookEvents *HookEventStore // in-memory hook event buffer; nil disables hook event tracking
NetworkEvents *NetworkEventStore // in-memory ambient network activity buffer; nil disables
IssueFetcher func() ([]TrackerIssuesResult, error) // injected; fetches issues from configured trackers
TrackerDiagnoser func(dir string) []tracker.TrackerStatus // injected; diagnoses tracker status with vault resolution
Projects *ProjectRegistry // multi-project routing; nil means single-project mode
PendingConfirms *PendingConfirmStore // pending destructive operation confirmations; nil disables
StatsWriter *stats.Writer // async SQLite writer for tool event persistence; nil disables
StatsStore *stats.StatsStore // for query-time aggregation; nil disables tool-stats route
AuditSink *audit.Writer // records mutating tracker actions for the audit trail; nil disables
AuditStore *audit.Store // serves audit-query reads; nil disables audit-query route
AgentCleaner AgentCleaner // async agent cleanup; nil disables agent-stop-async route
VaultResolver *vault.Resolver // session-scoped vault resolver; reused across requests to avoid repeated op.exe calls
// BoardTransitioner applies a board-transition request (advancing a card
// one pipeline stage). nil disables the board-transition route.
BoardTransitioner func(req BoardTransitionRequest) error
// contains filtered or unexported fields
}
Server listens for incoming client connections and executes CLI commands.
func (*Server) ListenAndServe ¶
ListenAndServe starts the TCP listener and blocks until ctx is cancelled. On shutdown it waits for all in-flight handler goroutines to return before closing, so a client request that's already accepted is never torn down mid-flight by listener close alone.
type SubscribeEvent ¶
type SubscribeEvent struct {
Type string `json:"type"` // "change", "agent-stopped"
AgentName string `json:"agent,omitempty"` // set for agent lifecycle events
}
SubscribeEvent is a notification sent over a persistent subscribe connection. For "agent-stopped" events, AgentName identifies the agent to remove immediately without waiting for the next discovery cycle.
type TrackerIssuesResult ¶
type TrackerIssuesResult struct {
TrackerName string `json:"tracker_name"`
TrackerKind string `json:"tracker_kind"`
TrackerRole string `json:"tracker_role,omitempty"`
Project string `json:"project"`
Issues []tracker.Issue `json:"issues"`
ReadyForReview []string `json:"ready_for_review,omitempty"`
// ReadyForReviewPRs maps an engineering ticket key to the pull-request URL
// carried on its handoff comment's optional `pr:` line, when present.
ReadyForReviewPRs map[string]string `json:"ready_for_review_prs,omitempty"`
// BoardCards is the derived pipeline placement per PM issue key, for the
// drag-board GUI. It is PM-role-only (maps a PM issue key → its derived
// BoardCard) and is left nil on engineering-tracker results.
BoardCards map[string]BoardCard `json:"board_cards,omitempty"`
Err string `json:"error,omitempty"`
}
TrackerIssuesResult is the wire type for a single tracker/project's issues.
ReadyForReview carries the engineering ticket keys that a PM tracker has currently flagged for review via a [human:ready-for-review] comment. It is populated on engineering-tracker results (where the keys actually live) so the TUI can join it against Issues without a separate lookup. See cli/CLAUDE.md "Review handoff" for the comment convention.
func GetTrackerIssues ¶
func GetTrackerIssues(addr, token string) ([]TrackerIssuesResult, error)
GetTrackerIssues fetches open issues from all configured tracker projects via the daemon.