From 584b0e9d77466a992e66b0d30ee41f07bf69446d Mon Sep 17 00:00:00 2001
From: artin <artin@cat.ms>
Date: Sat, 25 Jan 2025 22:29:55 +0800
Subject: [PATCH] fix: resolve nushell support issue

---
 README.md                      | 14 +++---
 cmd/commands/activate.go       | 30 +++---------
 cmd/commands/env.go            | 84 +++++++---------------------------
 docs/guides/quick-start.md     |  2 +-
 internal/manager.go            | 84 ++++++++++++++++++++++++++++++++++
 internal/shell/bash.go         |  2 +-
 internal/shell/clink.go        |  2 +-
 internal/shell/fish.go         |  2 +-
 internal/shell/nushell.go      | 76 +++++++++++++++++++-----------
 internal/shell/nushell_test.go | 63 +------------------------
 internal/shell/powershell.go   |  2 +-
 internal/shell/shell.go        |  7 ++-
 internal/shell/zsh.go          |  2 +-
 13 files changed, 178 insertions(+), 192 deletions(-)

diff --git a/README.md b/README.md
index 64f18c16..347737aa 100644
--- a/README.md
+++ b/README.md
@@ -9,10 +9,11 @@
 [![GitHub Release](https://img.shields.io/github/v/release/version-fox/vfox?display_name=tag&style=for-the-badge)](https://github.com/version-fox/vfox/releases)
 [![Discord](https://img.shields.io/discord/1191981003204477019?style=for-the-badge&logo=discord)](https://discord.gg/85c8ptYgb7)
 
-[[English]](./README.md)  [[中文文档]](./README_CN.md)
+[[English]](./README.md) [[中文文档]](./README_CN.md)
 
 If you **switch between development projects which expect different environments**, specifically different runtime versions or ambient libraries,
 or **you are tired of all kinds of cumbersome environment configurations**, `vfox` is the ideal choice for you.
+
 ## Introduction
 
 **`vfox` is a cross-platform version manager(similar to `nvm`, `fvm`, `sdkman`, `asdf-vm`, etc.), extendable via plugins**. It allows you to quickly install
@@ -57,13 +58,14 @@ if (-not (Test-Path -Path $PROFILE)) { New-Item -Type File -Path $PROFILE -Force
 # 3. copy internal/shell/clink_vfox.lua to script path
 
 # For Nushell:
-vfox activate nushell | save --append $nu.config-path
+vfox activate nushell $nu.default-config-dir | save --append $nu.config-path
 ```
 
 > Remember to restart your shell to apply the changes.
 
 #### 3. Add an SDK plugin
-```bash 
+
+```bash
 $ vfox add nodejs
 ```
 
@@ -93,8 +95,8 @@ Our future plans and high priority features and enhancements are:
   - Introducing plugin templates to facilitate multi-file plugin development.
   - Establishing a global registry (similar to `NPM Registry` or `Scoop Main Bucket`) to provide a unified entry point for plugin distribution.
   - Decomposing the existing plugin repository into individual repositories, one for each plugin.
-- [X] Allowing the switching of registry addresses.
-- [X] Plugin capabilities: Parsing legacy configuration files, such as `.nvmrc`, `.node-version`, `.sdkmanrc`, etc.
+- [x] Allowing the switching of registry addresses.
+- [x] Plugin capabilities: Parsing legacy configuration files, such as `.nvmrc`, `.node-version`, `.sdkmanrc`, etc.
 - [ ] Plugin capabilities: Allowing plugins to load installed runtimes and provide information about the runtime.
 
 ## Available Plugins
@@ -126,7 +128,6 @@ Plugin Contributions, please go to [Public Registry](https://github.com/version-
 
 ## Thanks
 
-
 > Thanks JetBrains for the free open source license. :)
 
 <a href="https://www.jetbrains.com/?from=gev" target="_blank">
@@ -139,4 +140,3 @@ Plugin Contributions, please go to [Public Registry](https://github.com/version-
 
 [Apache 2.0 license](./LICENSE) - Copyright (C) 2024 Han Li
 and [contributors](https://github.com/version-fox/vfox/graphs/contributors)
-
diff --git a/cmd/commands/activate.go b/cmd/commands/activate.go
index 7d05e1b4..6a7ef4da 100644
--- a/cmd/commands/activate.go
+++ b/cmd/commands/activate.go
@@ -22,8 +22,6 @@ import (
 	"strings"
 	"text/template"
 
-	"github.com/version-fox/vfox/internal/toolset"
-
 	"github.com/version-fox/vfox/internal"
 
 	"github.com/urfave/cli/v2"
@@ -46,26 +44,7 @@ func activateCmd(ctx *cli.Context) error {
 	manager := internal.NewSdkManager()
 	defer manager.Close()
 
-	workToolVersion, err := toolset.NewToolVersion(manager.PathMeta.WorkingDirectory)
-	if err != nil {
-		return err
-	}
-
-	if err = manager.ParseLegacyFile(func(sdkname, version string) {
-		if _, ok := workToolVersion.Record[sdkname]; !ok {
-			workToolVersion.Record[sdkname] = version
-		}
-	}); err != nil {
-		return err
-	}
-	homeToolVersion, err := toolset.NewToolVersion(manager.PathMeta.HomePath)
-	if err != nil {
-		return err
-	}
-	sdkEnvs, err := manager.EnvKeys(toolset.MultiToolVersions{
-		workToolVersion,
-		homeToolVersion,
-	}, internal.ShellLocation)
+	sdkEnvs, err := manager.FullEnvKeys()
 	if err != nil {
 		return err
 	}
@@ -91,7 +70,12 @@ func activateCmd(ctx *cli.Context) error {
 		return fmt.Errorf("unknown target shell %s", name)
 	}
 	exportStr := s.Export(exportEnvs)
-	str, err := s.Activate()
+	str, err := s.Activate(
+		shell.ActivateConfig{
+			SelfPath: path,
+			Args:     ctx.Args().Tail(),
+		},
+	)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/commands/env.go b/cmd/commands/env.go
index 49e4cafb..b71399da 100644
--- a/cmd/commands/env.go
+++ b/cmd/commands/env.go
@@ -19,14 +19,12 @@ package commands
 import (
 	"encoding/json"
 	"fmt"
+
 	"github.com/urfave/cli/v2"
 	"github.com/version-fox/vfox/internal"
-	"github.com/version-fox/vfox/internal/cache"
 	"github.com/version-fox/vfox/internal/env"
-	"github.com/version-fox/vfox/internal/logger"
 	"github.com/version-fox/vfox/internal/shell"
 	"github.com/version-fox/vfox/internal/toolset"
-	"path/filepath"
 )
 
 var Env = &cli.Command{
@@ -47,6 +45,10 @@ var Env = &cli.Command{
 			Aliases: []string{"j"},
 			Usage:   "output json format",
 		},
+		&cli.BoolFlag{
+			Name:  "full",
+			Usage: "output full env",
+		},
 	},
 	Action:   envCmd,
 	Category: CategorySDK,
@@ -57,8 +59,10 @@ func envCmd(ctx *cli.Context) error {
 		return outputJSON()
 	} else if ctx.IsSet("cleanup") {
 		return cleanTmp()
+	} else if ctx.IsSet("full") {
+		return envFlag(ctx, "full")
 	} else {
-		return envFlag(ctx)
+		return envFlag(ctx, "cwd")
 	}
 }
 
@@ -109,7 +113,7 @@ func cleanTmp() error {
 	return nil
 }
 
-func envFlag(ctx *cli.Context) error {
+func envFlag(ctx *cli.Context, mode string) error {
 	shellName := ctx.String("shell")
 	if shellName == "" {
 		return cli.Exit("shell name is required", 1)
@@ -120,13 +124,19 @@ func envFlag(ctx *cli.Context) error {
 	}
 	manager := internal.NewSdkManager()
 	defer manager.Close()
+	var sdkEnvs internal.SdkEnvs
+	var err error
+	if mode == "full" {
+		sdkEnvs, err = manager.FullEnvKeys()
+	} else {
+		sdkEnvs, err = manager.AggregateEnvKeys()
+	}
 
-	sdkEnvs, err := aggregateEnvKeys(manager)
 	if err != nil {
 		return err
 	}
 
-	if len(sdkEnvs) == 0 && shellName != "nushell" {
+	if len(sdkEnvs) == 0 {
 		return nil
 	}
 
@@ -145,63 +155,3 @@ func envFlag(ctx *cli.Context) error {
 	fmt.Println(exportStr)
 	return nil
 }
-
-func aggregateEnvKeys(manager *internal.Manager) (internal.SdkEnvs, error) {
-	workToolVersion, err := toolset.NewToolVersion(manager.PathMeta.WorkingDirectory)
-	if err != nil {
-		return nil, err
-	}
-
-	if err = manager.ParseLegacyFile(func(sdkname, version string) {
-		if _, ok := workToolVersion.Record[sdkname]; !ok {
-			workToolVersion.Record[sdkname] = version
-		}
-	}); err != nil {
-		return nil, err
-	}
-
-	curToolVersion, err := toolset.NewToolVersion(manager.PathMeta.CurTmpPath)
-	if err != nil {
-		return nil, err
-	}
-	defer curToolVersion.Save()
-
-	// Add the working directory to the first
-	tvs := toolset.MultiToolVersions{workToolVersion, curToolVersion}
-
-	flushCache, err := cache.NewFileCache(filepath.Join(manager.PathMeta.CurTmpPath, "flush_env.cache"))
-	if err != nil {
-		return nil, err
-	}
-	defer flushCache.Close()
-
-	var sdkEnvs []*internal.SdkEnv
-
-	tvs.FilterTools(func(name, version string) bool {
-		if lookupSdk, err := manager.LookupSdk(name); err == nil {
-			vv, ok := flushCache.Get(name)
-			if ok && string(vv) == version {
-				logger.Debugf("Hit cache, skip flush environment, %s@%s\n", name, version)
-				return true
-			} else {
-				logger.Debugf("No hit cache, name: %s cache: %s, expected: %s \n", name, string(vv), version)
-			}
-			v := internal.Version(version)
-			if keys, err := lookupSdk.EnvKeys(v, internal.ShellLocation); err == nil {
-				flushCache.Set(name, cache.Value(version), cache.NeverExpired)
-
-				sdkEnvs = append(sdkEnvs, &internal.SdkEnv{
-					Sdk: lookupSdk, Env: keys,
-				})
-
-				// If we encounter a .tool-versions file, it is valid for the entire shell session,
-				// unless we encounter the next .tool-versions file or manually switch to the use command.
-				curToolVersion.Record[name] = version
-				return true
-			}
-		}
-		return false
-	})
-
-	return sdkEnvs, nil
-}
diff --git a/docs/guides/quick-start.md b/docs/guides/quick-start.md
index 5dc5baa1..7a27c06f 100644
--- a/docs/guides/quick-start.md
+++ b/docs/guides/quick-start.md
@@ -132,7 +132,7 @@ y
 ::: details Nushell
 
 ```shell
-vfox activate nushell | save --append $nu.config-path
+vfox activate nushell $nu.default-config-dir | save --append $nu.config-path
 ```
 
 :::
diff --git a/internal/manager.go b/internal/manager.go
index 9eb829a7..afc403fa 100644
--- a/internal/manager.go
+++ b/internal/manager.go
@@ -34,6 +34,7 @@ import (
 	"github.com/mitchellh/go-ps"
 	"github.com/pterm/pterm"
 	"github.com/urfave/cli/v2"
+	"github.com/version-fox/vfox/internal/cache"
 	"github.com/version-fox/vfox/internal/config"
 	"github.com/version-fox/vfox/internal/env"
 	"github.com/version-fox/vfox/internal/logger"
@@ -69,6 +70,89 @@ type Manager struct {
 	Config     *config.Config
 }
 
+func (m *Manager) FullEnvKeys() (SdkEnvs, error) {
+	workToolVersion, err := toolset.NewToolVersion(m.PathMeta.WorkingDirectory)
+	if err != nil {
+		return nil, err
+	}
+
+	if err = m.ParseLegacyFile(func(sdkname, version string) {
+		if _, ok := workToolVersion.Record[sdkname]; !ok {
+			workToolVersion.Record[sdkname] = version
+		}
+	}); err != nil {
+		return nil, err
+	}
+	homeToolVersion, err := toolset.NewToolVersion(m.PathMeta.HomePath)
+	if err != nil {
+		return nil, err
+	}
+	return m.EnvKeys(toolset.MultiToolVersions{
+		workToolVersion,
+		homeToolVersion,
+	}, ShellLocation)
+}
+
+func (m *Manager) AggregateEnvKeys() (SdkEnvs, error) {
+	workToolVersion, err := toolset.NewToolVersion(m.PathMeta.WorkingDirectory)
+	if err != nil {
+		return nil, err
+	}
+
+	if err = m.ParseLegacyFile(func(sdkname, version string) {
+		if _, ok := workToolVersion.Record[sdkname]; !ok {
+			workToolVersion.Record[sdkname] = version
+		}
+	}); err != nil {
+		return nil, err
+	}
+
+	curToolVersion, err := toolset.NewToolVersion(m.PathMeta.CurTmpPath)
+	if err != nil {
+		return nil, err
+	}
+	defer curToolVersion.Save()
+
+	// Add the working directory to the first
+	tvs := toolset.MultiToolVersions{workToolVersion, curToolVersion}
+
+	flushCache, err := cache.NewFileCache(filepath.Join(m.PathMeta.CurTmpPath, "flush_env.cache"))
+	if err != nil {
+		return nil, err
+	}
+	defer flushCache.Close()
+
+	var sdkEnvs []*SdkEnv
+
+	tvs.FilterTools(func(name, version string) bool {
+		if lookupSdk, err := m.LookupSdk(name); err == nil {
+			vv, ok := flushCache.Get(name)
+			if ok && string(vv) == version {
+				logger.Debugf("Hit cache, skip flush environment, %s@%s\n", name, version)
+				return true
+			} else {
+				logger.Debugf("No hit cache, name: %s cache: %s, expected: %s \n", name, string(vv), version)
+			}
+			v := Version(version)
+			if keys, err := lookupSdk.EnvKeys(v, ShellLocation); err == nil {
+				flushCache.Set(name, cache.Value(version), cache.NeverExpired)
+
+				sdkEnvs = append(sdkEnvs, &SdkEnv{
+					Sdk: lookupSdk, Env: keys,
+				})
+
+				// If we encounter a .tool-versions file, it is valid for the entire shell session,
+				// unless we encounter the next .tool-versions file or manually switch to the use command.
+				curToolVersion.Record[name] = version
+				return true
+			}
+		}
+		return false
+	})
+
+	return sdkEnvs, nil
+}
+
 func (m *Manager) EnvKeys(tvs toolset.MultiToolVersions, location Location) (SdkEnvs, error) {
 	var sdkEnvs SdkEnvs
 	tools := make(map[string]struct{})
diff --git a/internal/shell/bash.go b/internal/shell/bash.go
index e9232ece..8c6898ab 100644
--- a/internal/shell/bash.go
+++ b/internal/shell/bash.go
@@ -50,7 +50,7 @@ type bash struct{}
 
 var Bash = bash{}
 
-func (b bash) Activate() (string, error) {
+func (b bash) Activate(config ActivateConfig) (string, error) {
 	return bashHook, nil
 }
 
diff --git a/internal/shell/clink.go b/internal/shell/clink.go
index ec129b02..f23f85de 100644
--- a/internal/shell/clink.go
+++ b/internal/shell/clink.go
@@ -31,7 +31,7 @@ type clink struct{}
 
 var Clink = clink{}
 
-func (b clink) Activate() (string, error) {
+func (b clink) Activate(config ActivateConfig) (string, error) {
 	return clinkHook, nil
 }
 
diff --git a/internal/shell/fish.go b/internal/shell/fish.go
index cce5cf0a..edf0b2b3 100644
--- a/internal/shell/fish.go
+++ b/internal/shell/fish.go
@@ -62,7 +62,7 @@ function cleanup_on_exit --on-process-exit %self
 end;
 `
 
-func (sh fish) Activate() (string, error) {
+func (sh fish) Activate(config ActivateConfig) (string, error) {
 	return fishHook, nil
 }
 
diff --git a/internal/shell/nushell.go b/internal/shell/nushell.go
index aec059c0..fb3a3e9a 100644
--- a/internal/shell/nushell.go
+++ b/internal/shell/nushell.go
@@ -3,38 +3,46 @@ package shell
 import (
 	"encoding/json"
 	"fmt"
-	"github.com/version-fox/vfox/internal/env"
+	"os"
 	"path/filepath"
+	"strings"
+
+	"github.com/version-fox/vfox/internal/env"
 )
 
 type nushell struct{}
 
 var Nushell = nushell{}
 
-const nushellConfig = env.Newline +
-	"# vfox configuration" + env.Newline +
-	"export-env {" + env.Newline +
-	"  def --env updateVfoxEnvironment [] {" + env.Newline +
-	"    let envData = (^'{{.SelfPath}}' env -s nushell | from json)" + env.Newline +
-	"    load-env $envData.envsToSet" + env.Newline +
-	"    hide-env ...$envData.envsToUnset" + env.Newline +
-	"  }" + env.Newline +
-	"  $env.config = ($env.config | upsert hooks.pre_prompt {" + env.Newline +
-	"    let currentValue = ($env.config | get -i hooks.pre_prompt)" + env.Newline +
-	"    if $currentValue == null {" + env.Newline +
-	"      [{updateVfoxEnvironment}]" + env.Newline +
-	"    } else {" + env.Newline +
-	"      $currentValue | append {updateVfoxEnvironment}" + env.Newline +
-	"    }" + env.Newline +
-	"  })" + env.Newline +
-	"  $env.__VFOX_SHELL = 'nushell'" + env.Newline +
-	"  $env.__VFOX_PID = $nu.pid" + env.Newline +
-	"  ^'{{.SelfPath}}' env --cleanup | ignore" + env.Newline +
-	"  updateVfoxEnvironment" + env.Newline +
-	"}" + env.Newline
-
-// Activate implements shell.Activate by returning a script to be placed in the Nushell configuration file. This script
-// does the following:
+const nushellConfig = `
+# vfox configuration
+# this make sure this configuration is up to date when you open a new shell
+^'{{.SelfPath}}' activate nushell $nu.default-config-dir | ignore
+
+export-env {
+  def --env updateVfoxEnvironment [] {
+    let envData = (^'{{.SelfPath}}' env -s nushell --full | from json)
+    load-env $envData.envsToSet
+    hide-env ...$envData.envsToUnset
+  }
+  $env.config = ($env.config | upsert hooks.pre_prompt {
+    let currentValue = ($env.config | get -i hooks.pre_prompt)
+    if $currentValue == null {
+      [{updateVfoxEnvironment}]
+    } else {
+      $currentValue | append {updateVfoxEnvironment}
+    }
+  })
+  $env.__VFOX_SHELL = 'nushell'
+  $env.__VFOX_PID = $nu.pid
+  ^'{{.SelfPath}}' env --cleanup | ignore
+  updateVfoxEnvironment
+}
+`
+
+// We create a `vfox.nu“ in the `$nu.default-config-dir“
+// Activate implements shell.Activate will generate a script to the `vfox.nu` file.
+// This script does the following:
 //
 // 1. Sets up a [pre_prompt hook] to update the environment variables when needed.
 // 2. Initializes the __VFOX_SHELL and __VFOX_PID environment variables.
@@ -42,8 +50,22 @@ const nushellConfig = env.Newline +
 // 4. Updates the environment variables.
 //
 // [pre_prompt hook]: https://www.nushell.sh/book/hooks.html
-func (n nushell) Activate() (string, error) {
-	return nushellConfig, nil
+func (n nushell) Activate(config ActivateConfig) (string, error) {
+	if len(config.Args) == 0 {
+		return "", fmt.Errorf("config path is required")
+	}
+
+	// write file to config
+	targetPath := filepath.Join(config.Args[0], "vfox.nu")
+
+	nushellConfig := strings.ReplaceAll(nushellConfig, "\n", env.Newline)
+	nushellConfig = strings.ReplaceAll(nushellConfig, "{{.SelfPath}}", config.SelfPath)
+
+	if err := os.WriteFile(targetPath, []byte(nushellConfig), 0755); err != nil {
+		return "", fmt.Errorf("failed to write file: %s", err)
+	}
+
+	return `source ($nu.default-config-dir | path join "vfox.nu")` + env.Newline, nil
 }
 
 // nushellExportData is used to create a JSON representation of the environment variables to be set and unset.
diff --git a/internal/shell/nushell_test.go b/internal/shell/nushell_test.go
index e5e1a80d..47e7a51a 100644
--- a/internal/shell/nushell_test.go
+++ b/internal/shell/nushell_test.go
@@ -1,73 +1,14 @@
 package shell
 
 import (
-	"bytes"
 	"encoding/json"
-	"github.com/version-fox/vfox/internal/env"
 	"os"
 	"reflect"
-	"runtime"
 	"slices"
 	"testing"
-	"text/template"
-)
-
-func TestActivate(t *testing.T) {
-	var newline string
-	if runtime.GOOS == "windows" {
-		newline = "\r\n"
-	} else {
-		newline = "\n"
-	}
-	selfPath := "/path/to/vfox"
-	want := newline +
-		"# vfox configuration" + newline +
-		"export-env {" + newline +
-		"  def --env updateVfoxEnvironment [] {" + newline +
-		"    let envData = (^'" + selfPath + "' env -s nushell | from json)" + newline +
-		"    load-env $envData.envsToSet" + newline +
-		"    hide-env ...$envData.envsToUnset" + newline +
-		"  }" + newline +
-		"  $env.config = ($env.config | upsert hooks.pre_prompt {" + newline +
-		"    let currentValue = ($env.config | get -i hooks.pre_prompt)" + newline +
-		"    if $currentValue == null {" + newline +
-		"      [{updateVfoxEnvironment}]" + newline +
-		"    } else {" + newline +
-		"      $currentValue | append {updateVfoxEnvironment}" + newline +
-		"    }" + newline +
-		"  })" + newline +
-		"  $env.__VFOX_SHELL = 'nushell'" + newline +
-		"  $env.__VFOX_PID = $nu.pid" + newline +
-		"  ^'" + selfPath + "' env --cleanup | ignore" + newline +
-		"  updateVfoxEnvironment" + newline +
-		"}" + newline
-
-	n := nushell{}
-	gotTemplate, err := n.Activate()
-
-	if err != nil {
-		t.Errorf("Unexpected error: %v", err)
-		return
-	}
-
-	parsedTemplate, err := template.New("activate").Parse(gotTemplate)
-	if err != nil {
-		t.Errorf("Unexpected error parsing template: %v", err)
-		return
-	}
 
-	var buffer bytes.Buffer
-	err = parsedTemplate.Execute(&buffer, struct{ SelfPath string }{selfPath})
-	if err != nil {
-		t.Errorf("Unexpected error executing template: %v", err)
-		return
-	}
-
-	got := buffer.String()
-	if got != want {
-		t.Errorf("Output mismatch:\n\ngot=\n%v\n\nwant=\n%v", got, want)
-	}
-}
+	"github.com/version-fox/vfox/internal/env"
+)
 
 func TestExport(t *testing.T) {
 	sep := string(os.PathListSeparator)
diff --git a/internal/shell/powershell.go b/internal/shell/powershell.go
index 0cfd1e71..da323953 100644
--- a/internal/shell/powershell.go
+++ b/internal/shell/powershell.go
@@ -59,7 +59,7 @@ Register-EngineEvent -SourceIdentifier PowerShell.Exiting -SupportEvent -Action
 }
 `
 
-func (sh pwsh) Activate() (string, error) {
+func (sh pwsh) Activate(config ActivateConfig) (string, error) {
 	return hook, nil
 }
 
diff --git a/internal/shell/shell.go b/internal/shell/shell.go
index 89a2bfb7..07433369 100644
--- a/internal/shell/shell.go
+++ b/internal/shell/shell.go
@@ -22,10 +22,15 @@ import (
 	"github.com/version-fox/vfox/internal/env"
 )
 
+type ActivateConfig struct {
+	SelfPath string
+	Args     []string
+}
+
 type Shell interface {
 	// Activate generates a shell script to be placed in the shell's configuration file, which will set up initial
 	// environment variables and set a hook to update the environment variables when needed.
-	Activate() (string, error)
+	Activate(config ActivateConfig) (string, error)
 
 	// Export generates a string that can be used by the shell to set or unset the given environment variables. (The
 	// input specifies environment variables to be unset by giving them a nil value.)
diff --git a/internal/shell/zsh.go b/internal/shell/zsh.go
index c0e94650..ec4770e8 100644
--- a/internal/shell/zsh.go
+++ b/internal/shell/zsh.go
@@ -48,7 +48,7 @@ if [[ -z "$__VFOX_PID" ]]; then
 fi
 `
 
-func (z zsh) Activate() (string, error) {
+func (z zsh) Activate(config ActivateConfig) (string, error) {
 	return zshHook, nil
 }