refactor(cli): reduce framework command glue paths
This commit is contained in:
parent
8c88084181
commit
fcee5a0a36
3 changed files with 55 additions and 115 deletions
|
|
@ -45,11 +45,7 @@ type profileConfigStore interface {
|
||||||
SaveDefault(frameworkconfig.FileConfig[ProfileConfig]) (string, error)
|
SaveDefault(frameworkconfig.FileConfig[ProfileConfig]) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type secretStore interface {
|
type secretStore = frameworksecretstore.Store
|
||||||
SetSecret(name, label, secret string) error
|
|
||||||
GetSecret(name string) (string, error)
|
|
||||||
DeleteSecret(name string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type manifestLoader func(startDir string) (frameworkmanifest.File, string, error)
|
type manifestLoader func(startDir string) (frameworkmanifest.File, string, error)
|
||||||
type executableResolver func() (string, error)
|
type executableResolver func() (string, error)
|
||||||
|
|
@ -119,19 +115,7 @@ func NewAppWithDependencies(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Run(args []string) error {
|
func (a *App) Run(args []string) error {
|
||||||
if isDoctorHelpCommand(args) {
|
return a.runBootstrap(context.Background(), normalizeArgs(args))
|
||||||
return a.printDoctorHelp()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) > 0 && strings.TrimSpace(args[0]) == "doctor" {
|
|
||||||
return a.runDoctor(context.Background(), args[1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
if isGlobalHelpCommand(args) {
|
|
||||||
return a.printGlobalHelp()
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.runBootstrap(context.Background(), args)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) runBootstrap(ctx context.Context, args []string) error {
|
func (a *App) runBootstrap(ctx context.Context, args []string) error {
|
||||||
|
|
@ -156,7 +140,7 @@ func (a *App) runBootstrap(ctx context.Context, args []string) error {
|
||||||
return a.runConfigShow(ctx, inv.Args)
|
return a.runConfigShow(ctx, inv.Args)
|
||||||
},
|
},
|
||||||
ConfigTest: func(ctx context.Context, inv frameworkbootstrap.Invocation) error {
|
ConfigTest: func(ctx context.Context, inv frameworkbootstrap.Invocation) error {
|
||||||
return a.runConfigTest(ctx, inv.Args)
|
return a.runDoctor(ctx, inv.Args)
|
||||||
},
|
},
|
||||||
ConfigDelete: func(ctx context.Context, inv frameworkbootstrap.Invocation) error {
|
ConfigDelete: func(ctx context.Context, inv frameworkbootstrap.Invocation) error {
|
||||||
return a.runConfigDelete(ctx, inv.Args)
|
return a.runConfigDelete(ctx, inv.Args)
|
||||||
|
|
@ -168,84 +152,6 @@ func (a *App) runBootstrap(ctx context.Context, args []string) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func isGlobalHelpCommand(args []string) bool {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if len(args) != 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch strings.TrimSpace(args[0]) {
|
|
||||||
case "help", "-h", "--help":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isDoctorHelpCommand(args []string) bool {
|
|
||||||
if len(args) != 2 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
first := strings.TrimSpace(args[0])
|
|
||||||
second := strings.TrimSpace(args[1])
|
|
||||||
|
|
||||||
if first == "help" && second == "doctor" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if first == "doctor" && (second == "-h" || second == "--help") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) printGlobalHelp() error {
|
|
||||||
metadata := a.runtimeMetadata()
|
|
||||||
|
|
||||||
if _, err := fmt.Fprintf(a.stdout, "%s\n\n", metadata.Description); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprintf(a.stdout, "Usage:\n %s <command> [args]\n\n", metadata.BinaryName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprintln(a.stdout, "Common commands:"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
commands := []struct {
|
|
||||||
name string
|
|
||||||
description string
|
|
||||||
}{
|
|
||||||
{name: "setup", description: "Initialize or update local configuration."},
|
|
||||||
{name: "mcp", description: "Run the MCP server over stdio."},
|
|
||||||
{name: "config", description: "Inspect or test resolved configuration."},
|
|
||||||
{name: "doctor", description: "Run local diagnostics."},
|
|
||||||
{name: "update", description: "Run the self-update flow."},
|
|
||||||
{name: "version", description: "Print the binary version."},
|
|
||||||
}
|
|
||||||
for _, command := range commands {
|
|
||||||
if _, err := fmt.Fprintf(a.stdout, " %-7s %s\n", command.name, command.description); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := fmt.Fprintf(a.stdout, "\nDetailed help: %s help <command>\n", metadata.BinaryName)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) printDoctorHelp() error {
|
|
||||||
metadata := a.runtimeMetadata()
|
|
||||||
|
|
||||||
_, err := fmt.Fprintf(
|
|
||||||
a.stdout,
|
|
||||||
"Usage:\n %s doctor [--profile NAME]\n\nRun local diagnostics for config, wallet, manifest, and IMAP connectivity.\n",
|
|
||||||
metadata.BinaryName,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) runConfig(ctx context.Context, command string, args []string) error {
|
func (a *App) runConfig(ctx context.Context, command string, args []string) error {
|
||||||
if a.prompter == nil {
|
if a.prompter == nil {
|
||||||
return fmt.Errorf("config prompter is not configured")
|
return fmt.Errorf("config prompter is not configured")
|
||||||
|
|
@ -382,10 +288,6 @@ func (a *App) runConfigShow(ctx context.Context, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) runConfigTest(ctx context.Context, args []string) error {
|
|
||||||
return a.runDoctor(ctx, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) runConfigDelete(_ context.Context, args []string) error {
|
func (a *App) runConfigDelete(_ context.Context, args []string) error {
|
||||||
if a.configStore == nil {
|
if a.configStore == nil {
|
||||||
return fmt.Errorf("config store is not configured")
|
return fmt.Errorf("config store is not configured")
|
||||||
|
|
@ -896,3 +798,40 @@ func newUserFacingError(message string, err error) error {
|
||||||
err: err,
|
err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeArgs(args []string) []string {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
normalized := append([]string(nil), args...)
|
||||||
|
for i, arg := range normalized {
|
||||||
|
normalized[i] = strings.TrimSpace(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch normalized[0] {
|
||||||
|
case "doctor":
|
||||||
|
if len(normalized) == 1 {
|
||||||
|
return []string{frameworkbootstrap.CommandConfig, frameworkbootstrap.ConfigSubcommandTest}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastArg := normalized[len(normalized)-1]
|
||||||
|
if lastArg == "-h" || lastArg == "--help" {
|
||||||
|
return []string{"help", frameworkbootstrap.CommandConfig, frameworkbootstrap.ConfigSubcommandTest}
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(
|
||||||
|
[]string{frameworkbootstrap.CommandConfig, frameworkbootstrap.ConfigSubcommandTest},
|
||||||
|
normalized[1:]...,
|
||||||
|
)
|
||||||
|
case "help":
|
||||||
|
if len(normalized) > 1 && normalized[1] == "doctor" {
|
||||||
|
return append(
|
||||||
|
[]string{"help", frameworkbootstrap.CommandConfig, frameworkbootstrap.ConfigSubcommandTest},
|
||||||
|
normalized[2:]...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ func TestAppRunShowsUsageWhenNoArgsProvided(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
text := output.String()
|
text := output.String()
|
||||||
for _, snippet := range []string{"Usage:", "doctor", "version"} {
|
for _, snippet := range []string{"Usage:", "config", "version"} {
|
||||||
if !strings.Contains(text, snippet) {
|
if !strings.Contains(text, snippet) {
|
||||||
t.Fatalf("help output missing %q: %q", snippet, text)
|
t.Fatalf("help output missing %q: %q", snippet, text)
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +219,19 @@ func TestAppRunDoctorHelp(t *testing.T) {
|
||||||
if err := app.Run([]string{"doctor", "--help"}); err != nil {
|
if err := app.Run([]string{"doctor", "--help"}); err != nil {
|
||||||
t.Fatalf("doctor help returned error: %v", err)
|
t.Fatalf("doctor help returned error: %v", err)
|
||||||
}
|
}
|
||||||
if got := output.String(); !strings.Contains(got, "email-mcp doctor [--profile NAME]") {
|
if got := output.String(); !strings.Contains(got, "email-mcp config test [args]") {
|
||||||
|
t.Fatalf("unexpected doctor help output: %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppRunHelpDoctorUsesConfigTestHelp(t *testing.T) {
|
||||||
|
output := &bytes.Buffer{}
|
||||||
|
app := NewAppWithDependencies(nil, nil, nil, nil, nil, nil, nil, nil, output, &bytes.Buffer{}, "dev")
|
||||||
|
|
||||||
|
if err := app.Run([]string{"help", "doctor"}); err != nil {
|
||||||
|
t.Fatalf("help doctor returned error: %v", err)
|
||||||
|
}
|
||||||
|
if got := output.String(); !strings.Contains(got, "email-mcp config test [args]") {
|
||||||
t.Fatalf("unexpected doctor help output: %q", got)
|
t.Fatalf("unexpected doctor help output: %q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import (
|
||||||
frameworkcli "gitea.lclr.dev/AI/mcp-framework/cli"
|
frameworkcli "gitea.lclr.dev/AI/mcp-framework/cli"
|
||||||
frameworkconfig "gitea.lclr.dev/AI/mcp-framework/config"
|
frameworkconfig "gitea.lclr.dev/AI/mcp-framework/config"
|
||||||
frameworkmanifest "gitea.lclr.dev/AI/mcp-framework/manifest"
|
frameworkmanifest "gitea.lclr.dev/AI/mcp-framework/manifest"
|
||||||
frameworksecretstore "gitea.lclr.dev/AI/mcp-framework/secretstore"
|
|
||||||
frameworkupdate "gitea.lclr.dev/AI/mcp-framework/update"
|
frameworkupdate "gitea.lclr.dev/AI/mcp-framework/update"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -34,7 +33,7 @@ func (a *App) runDoctor(ctx context.Context, args []string) error {
|
||||||
metadata := a.runtimeMetadata()
|
metadata := a.runtimeMetadata()
|
||||||
report := frameworkcli.RunDoctor(ctx, frameworkcli.DoctorOptions{
|
report := frameworkcli.RunDoctor(ctx, frameworkcli.DoctorOptions{
|
||||||
ConfigCheck: frameworkcli.NewConfigCheck(frameworkconfig.NewStore[ProfileConfig](binaryName)),
|
ConfigCheck: frameworkcli.NewConfigCheck(frameworkconfig.NewStore[ProfileConfig](binaryName)),
|
||||||
SecretStoreCheck: frameworkcli.SecretStoreAvailabilityCheck(a.frameworkSecretStoreFactory()),
|
SecretStoreCheck: frameworkcli.SecretStoreAvailabilityCheck(a.openSecretStore),
|
||||||
ManifestDir: a.doctorManifestDir(),
|
ManifestDir: a.doctorManifestDir(),
|
||||||
ManifestValidator: func(file frameworkmanifest.File, _ string) []string {
|
ManifestValidator: func(file frameworkmanifest.File, _ string) []string {
|
||||||
return validateManifestUpdate(file, metadata.BinaryName)
|
return validateManifestUpdate(file, metadata.BinaryName)
|
||||||
|
|
@ -54,16 +53,6 @@ func (a *App) runDoctor(ctx context.Context, args []string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) frameworkSecretStoreFactory() func() (frameworksecretstore.Store, error) {
|
|
||||||
return func() (frameworksecretstore.Store, error) {
|
|
||||||
store, err := a.openSecretStore()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return store, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) doctorManifestDir() string {
|
func (a *App) doctorManifestDir() string {
|
||||||
if a.resolveExecutable == nil {
|
if a.resolveExecutable == nil {
|
||||||
return "."
|
return "."
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue