Documentation
¶
Overview ¶
Package calllog records every invocation of a sofia-repo tool to a shared JSON-lines log so calls can be analysed and optimised later.
Log location follows the XDG Base Directory spec: by default `$XDG_STATE_HOME/sofia/calls.jsonl` (→ `~/.local/state/sofia/calls.jsonl`). The location can be overridden with the SOFIA_LOG_DIR env var, which is what `sf history` advertises so developers can point the log at the repo's own working tree if they prefer.
Index ¶
- func Finalize(fallbackTool string, err error)
- func Fingerprint(args []string) string
- func Path() string
- func ProjectTag() string
- func RegisterPluginGroups(names []string)
- func Run(root *cobra.Command, fallbackTool string) error
- func SessionID() string
- func Source() string
- func Track(tool string, args []string, fn func(t *Tracker) error) error
- func UnderTest() bool
- type Counter
- type Entry
- type Tracker
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Finalize ¶
Finalize guarantees exactly one log entry per invocation. Tools log themselves via Start/Finish; this fills the gaps those miss — chiefly Cobra arg-validation errors that abort before a tool's RunE ever runs. Idempotent and safe: a no-op when the tool already finished.
func Fingerprint ¶
Fingerprint returns a stable, sort-independent identifier for a set of argument strings. Equivalent invocations (e.g. "A B" and "B A") share a fingerprint, so history can collapse them when surfacing repeated queries. The form is a 16-hex-char SHA-256 prefix — short enough to scan, long enough to avoid collisions across normal usage.
func Path ¶
func Path() string
Path returns the file used for the shared JSONL log. Resolution order:
- $SOFIA_LOG_DIR/calls.jsonl — explicit override (CI, devs that want in-tree logs).
- $XDG_STATE_HOME/sofia/calls.jsonl — XDG-conformant default.
- ~/.local/state/sofia/calls.jsonl — XDG fallback when the env var is unset (the spec's defined default).
- ./calls.jsonl — final fallback if we can't even discover HOME.
All sofia tools (master `sf` binary and project-specific binaries) hit the same path so history aggregates the full picture.
func ProjectTag ¶ added in v0.2.0
func ProjectTag() string
func RegisterPluginGroups ¶ added in v0.2.0
func RegisterPluginGroups(names []string)
RegisterPluginGroups records plugin group names that must be excluded from the central call-log fallback (their bare invocation only prints help). Additive and idempotent. The plugin subcommands themselves (`<plugin>.<command>`) still log, since they do real work and self-log via Start/Finish.
func Run ¶
Run executes root and logs the invocation centrally, then returns the command error to the caller (which prints/exits). fallbackTool names the tool when the command path can't — standalone binaries whose root *is* the tool. A tool that actually ran will have set its own canonical name, which takes precedence.
func Source ¶ added in v0.2.0
func Source() string
Source, SessionID and ProjectTag expose the same classification calllog stamps onto its own entries, so a subprocess plugin can be handed identical SOFIA_SOURCE / SOFIA_SESSION_ID / SOFIA_TAG values (see internal/plugin's invocation env) without a parallel, drifting implementation.
Types ¶
type Counter ¶
Counter wraps w to track both the byte count and an approximate LLM token count of everything written through it. Token cost is estimated per chunk via tokens.Estimate; chunk-boundary inaccuracy in the rare case of a Write splitting a multi-byte rune is bounded by a single token and irrelevant for analytics.
type Entry ¶
type Entry struct {
Timestamp string `json:"ts"`
Tool string `json:"tool"`
Source string `json:"source,omitempty"` // agent | manual | test (who invoked it)
SessionID string `json:"sid,omitempty"` // Claude Code session id (joins with `sf cc`)
Tag string `json:"tag,omitempty"` // project the call belongs to
Args []string `json:"args"`
Fingerprint string `json:"fp,omitempty"` // sorted+joined args for grouping equivalent invocations
DurationMs int64 `json:"dur_ms"`
ExitCode int `json:"exit"`
Error string `json:"err,omitempty"`
OutputBytes int64 `json:"out_bytes,omitempty"` // size of stdout payload, in bytes
OutputTokens int64 `json:"out_tokens,omitempty"` // approximate LLM tokens (see internal/tokens)
Summary map[string]any `json:"summary,omitempty"`
}
func Read ¶
Read loads every entry from the shared call log, in append order (oldest first). A missing log is not an error — it returns nil. Malformed lines are skipped so a single bad write can't break a reader. Used by tools that need the raw log (e.g. `sf gripe`'s list view, `sf doctor`'s pending-feedback count); `sf history` keeps its own filtering reader on top of the same file.
type Tracker ¶
type Tracker struct {
// contains filtered or unexported fields
}
Tracker measures an invocation. Pair Start(...) with .Finish(err, summary).
func (*Tracker) Finish ¶
Finish writes the log entry. Idempotent — a second call is a no-op, so a tool's own Finish and the central Finalize can't double-log. Errors writing the log are swallowed; we don't want logging to break the user's tool invocation.
func (*Tracker) RecordOutput ¶
RecordOutput copies both byte and token accumulators from a Counter in one call — the typical Run() epilogue.
func (*Tracker) SetExitCode ¶ added in v0.2.0
SetExitCode records a specific process exit code. Finish otherwise collapses any error to exit 1; a subprocess (plugin) that exited with its own non-zero code calls SetExitCode first so the real code is logged. Set before Finish.
func (*Tracker) SetOutputBytes ¶
SetOutputBytes records the size of the user-facing payload (typically stdout). History can use this to spot heavy outputs that should be trimmed or capped.
func (*Tracker) SetOutputTokens ¶
SetOutputTokens records the approximate LLM token count for the user-facing payload (see package `internal/tokens`).
func (*Tracker) SetSummary ¶
SetSummary mutates the pending summary map; safe to call multiple times.