返回 DeepSeek-Reasonix
windows_posix_script.go
根目录 / internal / hook / windows_posix_script.go
1 package hook
2
3 import (
4 "path/filepath"
5 "strings"
6
7 "reasonix/internal/pluginpkg"
8 )
9
10 func completePluginHookExecutionConfig(h pluginpkg.Hook, root, goos string, mode ExecutionMode) HookConfig {
11 candidate := expandPluginRoot(h.Command, root)
12 if candidate != "" && !filepath.IsAbs(candidate) {
13 candidate = filepath.Join(root, filepath.FromSlash(candidate))
14 }
15 autoPOSIXScript := goos == "windows" && mode == ExecutionLegacy && h.Args == nil && isPOSIXShellScriptFile(candidate)
16 if autoPOSIXScript {
17 mode = ExecutionShell
18 h.Shell = "bash"
19 }
20 explicitBash := goos == "windows" && mode == ExecutionShell && strings.EqualFold(strings.TrimSpace(h.Shell), "bash")
21 expansionRoot := root
22 if explicitBash {
23 expansionRoot = strings.ReplaceAll(root, `\`, "/")
24 }
25 command := expandPluginRoot(h.Command, expansionRoot)
26 resolveFromPluginRoot := (mode != ExecutionShell || autoPOSIXScript) &&
27 !(mode == ExecutionExec && h.PayloadFormat == "claude")
28 if command != "" && resolveFromPluginRoot && !filepath.IsAbs(command) {
29 command = filepath.Join(root, filepath.FromSlash(command))
30 }
31 if mode == ExecutionLegacy {
32 command = NormalizeCommand(command)
33 } else if goos == "windows" && windowsShellMayUsePOSIXPath(h.Shell) {
34 if scriptPath, ok := windowsPOSIXScriptPath(command, root); ok {
35 h.Shell = "bash"
36 command = bashSingleQuote(filepath.ToSlash(scriptPath))
37 }
38 }
39 var argv []string
40 if h.ArgsSet {
41 argv = make([]string, 0, len(h.Args))
42 }
43 for _, arg := range h.Args {
44 argv = append(argv, expandPluginRoot(arg, root))
45 }
46 return HookConfig{
47 Command: command,
48 Argv: argv,
49 ExecutionMode: mode,
50 Shell: h.Shell,
51 }
52 }
53
54 func bashSingleQuote(value string) string {
55 if value == "" {
56 return "''"
57 }
58 return "'" + strings.ReplaceAll(value, "'", "'\\''") + "'"
59 }
60
61 func windowsShellMayUsePOSIXPath(shell string) bool {
62 switch strings.ToLower(strings.TrimSpace(shell)) {
63 case "", "auto", "bash":
64 return true
65 default:
66 return false
67 }
68 }
69
70 func windowsPOSIXScriptPath(command, cwd string) (string, bool) {
71 raw := strings.TrimSpace(command)
72 rawPath := filepath.FromSlash(raw)
73 if filepath.IsAbs(rawPath) && isPOSIXShellScriptFile(rawPath) {
74 return filepath.Clean(rawPath), true
75 }
76 if len(raw) >= 2 && ((raw[0] == '"' && raw[len(raw)-1] == '"') || (raw[0] == '\'' && raw[len(raw)-1] == '\'')) {
77 path := filepath.FromSlash(raw[1 : len(raw)-1])
78 if filepath.IsAbs(path) && isPOSIXShellScriptFile(path) {
79 return filepath.Clean(path), true
80 }
81 }
82 fields, _, _, ok := parseSimpleHookCommandFields(command)
83 if !ok || len(fields) != 1 {
84 return "", false
85 }
86 path := filepath.FromSlash(fields[0])
87 if !filepath.IsAbs(path) && strings.TrimSpace(cwd) != "" {
88 path = filepath.Join(cwd, path)
89 }
90 if !isPOSIXShellScriptFile(path) {
91 return "", false
92 }
93 return filepath.Clean(path), true
94 }
95
96 func normalizeWindowsHookSpawnInputForPlatform(in SpawnInput, goos string) SpawnInput {
97 if goos != "windows" || in.Mode != ExecutionLegacy || in.Args != nil {
98 return in
99 }
100 if scriptPath, ok := windowsPOSIXScriptPath(in.Command, in.Cwd); ok {
101 in.Mode = ExecutionShell
102 in.Shell = "bash"
103 in.Command = bashSingleQuote(filepath.ToSlash(scriptPath))
104 }
105 return in
106 }
107
108 func requiresWindowsBashForHook(config HookConfig) bool {
109 switch config.ExecutionMode {
110 case ExecutionShell:
111 if strings.EqualFold(strings.TrimSpace(config.Shell), "bash") {
112 return true
113 }
114 return windowsShellMayUsePOSIXPath(config.Shell) && func() bool {
115 _, ok := windowsPOSIXScriptPath(config.Command, config.Cwd)
116 return ok
117 }()
118 case ExecutionExec:
119 return isBarePOSIXShellWord(config.Command) && hasCommandStringFlag(config.Argv)
120 case ExecutionLegacy:
121 if config.Argv != nil {
122 return isBarePOSIXShellWord(config.Command) && hasCommandStringFlag(config.Argv)
123 }
124 if _, ok := windowsPOSIXScriptPath(config.Command, config.Cwd); ok {
125 return true
126 }
127 fields, _, _, ok := parseSimpleHookCommandFields(config.Command)
128 return ok && len(fields) >= 3 && isBarePOSIXShellWord(fields[0]) && hasCommandStringFlag(fields[1:])
129 default:
130 return false
131 }
132 }
133
133 lines GO