Files
mesh-catalog/modules/claude-code/cmd/claude-code/registrations.go
T
jochen e139fb4dd9 claude-code: set shared agent text at the controller's terminal, for now
Instructions, output styles, skills, commands and subagents for every node
or some reach every session as the mesh's own word, and the tools cannot
tell an agent from the operator; the operator decided they are set at the
terminal until a surface of their own updates them (hq issue 340). They
are settings keys, each text a list of lines; the home scope stays free.
2026-10-09 09:41:52 +02:00

108 lines
3.3 KiB
Go

package main
// What a Claude Code session on the mesh runs or obeys, and where each came from (novox/hq issue 340): for the
// operator to review what is registered today, read-only.
import (
"encoding/json"
"sort"
"strings"
)
// ByIs says what a registration's `by` is: the node whose tool runner served the call, never the caller, whom the
// runner is not told.
const ByIs = "the node whose tool runner served the registering call; who called it was never recorded, because the runner is not told"
// Registrations is every registration on the mesh that reaches a session, read from the state, and what the
// operator set at the controller's terminal for this node: the tool servers (`servers`), the agent's
// configuration (`config`) with whether each is still rendered and, when not, why; and the `managed_settings`
// and `mcp_servers` this node's layers compose to.
func Registrations(p Paths, servers ServerState, config ConfigState) (map[string]any, error) {
serverKeys, err := servers.Keys()
if err != nil {
return nil, err
}
sort.Strings(serverKeys)
registered := map[string]any{}
for _, key := range serverKeys {
raw, found, err := servers.Get(key)
if err != nil || !found {
continue
}
var entry map[string]any
if json.Unmarshal(raw, &entry) != nil {
continue
}
row := map[string]any{"entry": entry, "by": "not recorded: a tool server's registration kept only its entry"}
if _, name, ok := strings.Cut(key, "."); ok {
if problem := EntryProblem(name, entry); problem != "" {
row["rendered"] = "no: " + problem
} else {
row["rendered"] = "yes, on the nodes its key names"
}
}
registered[key] = row
}
configKeys, err := config.Keys()
if err != nil {
return nil, err
}
sort.Strings(configKeys)
items := map[string]any{}
for _, key := range configKeys {
raw, found, err := config.Get(key)
if err != nil || !found {
continue
}
var it Item
if json.Unmarshal(raw, &it) != nil {
continue
}
_, node, _, _, ok := ParseKey(key)
if !ok {
continue
}
row := map[string]any{"kind": it.Kind, "scope": it.Scope, "by": it.By, "at": it.At}
files := make([]string, 0, len(it.Files))
for f := range it.Files {
files = append(files, f)
}
sort.Strings(files)
if len(files) > 0 {
row["files"] = files
}
switch it.Kind {
case KindHook:
row["event"], row["matcher"], row["command"] = it.Event, it.Matcher, it.Command
case KindSettings:
row["settings"] = it.Settings
}
switch problem := it.Problem(); {
case it.Key(node) != key:
row["rendered"] = "no: its key does not say what the item is"
case problem != "":
row["rendered"] = "no: " + problem
default:
row["rendered"] = "yes, as " + it.Scope + " " + it.Kind
}
items[key] = row
}
var settings Settings
_ = readJSON(p.Settings, &settings)
text, leftOut := terminalText(p)
shared := []string{}
for _, it := range text {
shared = append(shared, it.Kind+" "+it.Name)
}
return map[string]any{
"set at the controller's terminal, as this node composes it": map[string]any{
"managed_settings": settings.ManagedSettings, "mcp_servers": settings.MCPServers,
"shared text": shared, "shared text left out": leftOut},
"tool servers registered through this module": registered,
"configuration registered through this module": items,
"by": ByIs,
}, nil
}