Files
zero/internal/plugins/info.go
Felix-Ayush 2479884dea feat(plugins): add zero plugins info command (#773)
* feat(plugins): add zero plugins info command

Add plugins info to inspect manifest state, extension counts, and
lockfile source/hash with optional hash drift detection.

* fix(plugins): derive lock dir from plugin install path for info

Remove LockDir from InfoOptions and read the lockfile from
filepath.Dir(plugin.PluginDir) so lock metadata matches the resolved
plugin location. Consolidate duplicate lock hash output lines.
2026-07-22 08:14:00 +08:00

63 lines
1.6 KiB
Go

package plugins
import (
"errors"
"fmt"
"path/filepath"
"strings"
)
// ErrNotInstalled is returned when Info cannot find a plugin with the requested id.
var ErrNotInstalled = errors.New("plugin is not installed")
// PluginInfo combines a loaded plugin with optional lockfile metadata.
type PluginInfo struct {
Plugin LoadedPlugin `json:"plugin"`
LockSource string `json:"lockSource,omitempty"`
LockHash string `json:"lockHash,omitempty"`
HashDrift bool `json:"hashDrift,omitempty"`
}
// InfoOptions controls plugin lookup and lockfile resolution for Info.
type InfoOptions struct {
LoadOptions LoadOptions
}
// Info returns details for the named plugin after normal discovery precedence.
func Info(options InfoOptions, id string) (PluginInfo, error) {
id = strings.TrimSpace(id)
if id == "" {
return PluginInfo{}, errors.New("plugin id is required")
}
result, err := Load(options.LoadOptions)
if err != nil {
return PluginInfo{}, err
}
var plugin LoadedPlugin
found := false
for _, candidate := range result.Plugins {
if candidate.ID == id {
plugin = candidate
found = true
break
}
}
if !found {
return PluginInfo{}, fmt.Errorf("%w: %q", ErrNotInstalled, id)
}
info := PluginInfo{Plugin: plugin}
if lock, readErr := ReadLock(filepath.Dir(plugin.PluginDir)); readErr == nil {
if entry, ok := lock[plugin.ID]; ok {
info.LockSource = entry.Source
info.LockHash = entry.Hash
}
}
if info.LockHash != "" {
if current, hashErr := hashTree(plugin.PluginDir); hashErr == nil {
info.HashDrift = current != info.LockHash
}
}
return info, nil
}