Documentation
¶
Overview ¶
Package devicescan inventories local AI client configuration on a device.
Scan reads known config locations under a home directory (provided as an fs.FS), parses MCP server, skill, and plugin observations, and returns a types.DeviceScan suitable for submission to the Obot backend.
Each client is integrated as a value type implementing ClientScanner in its own file (claudecode.go, codex.go, …). The orchestrator below runs every scanner through a fixed pipeline: globals → glob walk → project hits → plugins → skills → presence → build.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DetectClaudeCodePresence ¶
func DetectClaudeCodePresence(home string) types.DeviceScanClient
func DetectCursorPresence ¶
func DetectCursorPresence(home string) types.DeviceScanClient
func Scan ¶
func Scan(ctx context.Context, fsys fs.FS, homeAbs string, maxDepth int) (types.DeviceScanManifest, error)
Scan runs the full collection pipeline against fsys (rooted at homeAbs) and returns the assembled DeviceScan. Per-phase errors are dropped (logged at debug level) so a missing or malformed config never aborts the rest of the scan. Context cancellation propagates.
Server-assigned envelope fields (ScannerVersion, ScannedAt, DeviceID, Hostname, OS, Arch, Username, ID, ReceivedAt, SubmittedBy) are left zero; the caller fills them in.
maxDepth caps how deep the project walk descends from the home root when looking for project-scope configs and SKILL.md files.
Types ¶
type ClientScanner ¶
type ClientScanner interface {
// Name is the wire `client` tag this scanner emits.
Name() string
// Presence returns the binary/app-bundle/config-dir signals used to
// decide whether this client is installed on the device, regardless
// of whether it has any config to scan. The orchestrator emits a
// clients[] row when any signal fires.
Presence() clientPresenceDef
// GlobalConfigPaths returns fs-relative paths the scanner opens during
// ScanGlobal. The orchestrator uses these to suppress redundant
// project-walk hits on the same path (e.g. ~/.cursor/mcp.json appears
// in both the global config list and the project glob).
GlobalConfigPaths() []string
// ProjectGlobs returns gobwas/glob patterns that match this client's
// project-scope config files (e.g. "**/.cursor/mcp.json"). The
// orchestrator runs a single fs.WalkDir, matches every file against
// every scanner's globs, and dispatches hits to ScanProject.
//
// Empty for clients with no project-scope config.
ProjectGlobs() []string
// ScanGlobal opens this client's global config(s) and returns emitted
// MCP server observations. May call s.addFile to record config files.
ScanGlobal(s *scanState) []types.DeviceScanMCPServer
// ScanProject parses one project-scope config file (already known to
// match one of ProjectGlobs) and returns emitted MCP server
// observations.
ScanProject(s *scanState, configRel string) []types.DeviceScanMCPServer
}
ClientScanner is the per-client integration surface. Each AI client (Claude Code, Cursor, etc.) implements this with one struct in its own file. Methods are pure: they read the fs and return observations instead of mutating shared state.
State that genuinely is shared (file table, client table) lives on scanState and is updated via its methods (addFile, addClient).
type PluginScanner ¶
type PluginScanner interface {
// ScanPlugins returns plugin observations and any nested MCP/skill
// observations discovered alongside them.
ScanPlugins(s *scanState) (
plugins []types.DeviceScanPlugin,
servers []types.DeviceScanMCPServer,
skills []types.DeviceScanSkill,
)
}
PluginScanner is implemented by clients that emit DeviceScanPlugin observations alongside MCP scanning. Returning slices keeps the orchestrator the single point of accumulation.