@linxiraos/pi-utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/CHANGELOG.md +449 -0
  2. package/README.md +36 -0
  3. package/dist/types/abortable.d.ts +32 -0
  4. package/dist/types/acp/connection.d.ts +118 -0
  5. package/dist/types/acp/protocol.d.ts +526 -0
  6. package/dist/types/acp/schema.d.ts +41 -0
  7. package/dist/types/acp/stream.d.ts +8 -0
  8. package/dist/types/acp/transport.d.ts +81 -0
  9. package/dist/types/acp.d.ts +6 -0
  10. package/dist/types/async.d.ts +20 -0
  11. package/dist/types/binary.d.ts +17 -0
  12. package/dist/types/browsers.d.ts +68 -0
  13. package/dist/types/chalk.d.ts +125 -0
  14. package/dist/types/cli.d.ts +134 -0
  15. package/dist/types/color.d.ts +102 -0
  16. package/dist/types/dates.d.ts +7 -0
  17. package/dist/types/dirs.d.ts +282 -0
  18. package/dist/types/docx/converter.d.ts +46 -0
  19. package/dist/types/docx/xml.d.ts +26 -0
  20. package/dist/types/docx/zip.d.ts +6 -0
  21. package/dist/types/docx.d.ts +11 -0
  22. package/dist/types/dom/core.d.ts +431 -0
  23. package/dist/types/dom/parser.d.ts +7 -0
  24. package/dist/types/dom/selector.d.ts +5 -0
  25. package/dist/types/dom.d.ts +5 -0
  26. package/dist/types/env.d.ts +124 -0
  27. package/dist/types/fetch-retry.d.ts +96 -0
  28. package/dist/types/file-lock.d.ts +23 -0
  29. package/dist/types/format.d.ts +37 -0
  30. package/dist/types/frontmatter.d.ts +46 -0
  31. package/dist/types/fs-error.d.ts +31 -0
  32. package/dist/types/glob.d.ts +28 -0
  33. package/dist/types/headers.d.ts +34 -0
  34. package/dist/types/index.d.ts +39 -0
  35. package/dist/types/json-parse.d.ts +74 -0
  36. package/dist/types/json.d.ts +14 -0
  37. package/dist/types/logger/rotating-file.d.ts +18 -0
  38. package/dist/types/logger.d.ts +96 -0
  39. package/dist/types/loop-phase.d.ts +10 -0
  40. package/dist/types/lru.d.ts +46 -0
  41. package/dist/types/marked/core.d.ts +445 -0
  42. package/dist/types/marked.d.ts +2 -0
  43. package/dist/types/mermaid-ascii.d.ts +11 -0
  44. package/dist/types/mime.d.ts +29 -0
  45. package/dist/types/module-timer.d.ts +1 -0
  46. package/dist/types/path-tree.d.ts +76 -0
  47. package/dist/types/path.d.ts +2 -0
  48. package/dist/types/peek-file.d.ts +29 -0
  49. package/dist/types/postmortem.d.ts +104 -0
  50. package/dist/types/process-name.d.ts +7 -0
  51. package/dist/types/procmgr.d.ts +74 -0
  52. package/dist/types/prompt.d.ts +18 -0
  53. package/dist/types/ptree.d.ts +110 -0
  54. package/dist/types/readability/readability.d.ts +9 -0
  55. package/dist/types/readability/readerable.d.ts +10 -0
  56. package/dist/types/readability/types.d.ts +70 -0
  57. package/dist/types/readability.d.ts +4 -0
  58. package/dist/types/ring.d.ts +93 -0
  59. package/dist/types/runtime-install.d.ts +78 -0
  60. package/dist/types/sanitize-text.d.ts +29 -0
  61. package/dist/types/snowflake.d.ts +25 -0
  62. package/dist/types/stderr-guard.d.ts +22 -0
  63. package/dist/types/stream.d.ts +67 -0
  64. package/dist/types/tab-spacing.d.ts +24 -0
  65. package/dist/types/temp.d.ts +17 -0
  66. package/dist/types/template.d.ts +62 -0
  67. package/dist/types/timing-buffer.d.ts +22 -0
  68. package/dist/types/tls-fetch.d.ts +37 -0
  69. package/dist/types/turndown/gfm.d.ts +11 -0
  70. package/dist/types/turndown/html.d.ts +5 -0
  71. package/dist/types/turndown/service.d.ts +21 -0
  72. package/dist/types/turndown/types.d.ts +70 -0
  73. package/dist/types/turndown.d.ts +4 -0
  74. package/dist/types/type-guards.d.ts +5 -0
  75. package/dist/types/vendor/mermaid-ascii/ascii/ansi.d.ts +41 -0
  76. package/dist/types/vendor/mermaid-ascii/ascii/canvas.d.ts +89 -0
  77. package/dist/types/vendor/mermaid-ascii/ascii/class-diagram.d.ts +7 -0
  78. package/dist/types/vendor/mermaid-ascii/ascii/converter.d.ts +12 -0
  79. package/dist/types/vendor/mermaid-ascii/ascii/draw.d.ts +66 -0
  80. package/dist/types/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +48 -0
  81. package/dist/types/vendor/mermaid-ascii/ascii/edge-routing.d.ts +43 -0
  82. package/dist/types/vendor/mermaid-ascii/ascii/er-diagram.d.ts +7 -0
  83. package/dist/types/vendor/mermaid-ascii/ascii/grid.d.ts +56 -0
  84. package/dist/types/vendor/mermaid-ascii/ascii/index.d.ts +65 -0
  85. package/dist/types/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +27 -0
  86. package/dist/types/vendor/mermaid-ascii/ascii/pathfinder.d.ts +17 -0
  87. package/dist/types/vendor/mermaid-ascii/ascii/sequence.d.ts +7 -0
  88. package/dist/types/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +11 -0
  89. package/dist/types/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +34 -0
  90. package/dist/types/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +11 -0
  91. package/dist/types/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +11 -0
  92. package/dist/types/vendor/mermaid-ascii/ascii/shapes/index.d.ts +26 -0
  93. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +31 -0
  94. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +11 -0
  95. package/dist/types/vendor/mermaid-ascii/ascii/shapes/special.d.ts +59 -0
  96. package/dist/types/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +17 -0
  97. package/dist/types/vendor/mermaid-ascii/ascii/shapes/state.d.ts +30 -0
  98. package/dist/types/vendor/mermaid-ascii/ascii/shapes/types.d.ts +55 -0
  99. package/dist/types/vendor/mermaid-ascii/ascii/types.d.ts +206 -0
  100. package/dist/types/vendor/mermaid-ascii/ascii/validate.d.ts +51 -0
  101. package/dist/types/vendor/mermaid-ascii/ascii/xychart.d.ts +2 -0
  102. package/dist/types/vendor/mermaid-ascii/class/parser.d.ts +6 -0
  103. package/dist/types/vendor/mermaid-ascii/class/types.d.ts +102 -0
  104. package/dist/types/vendor/mermaid-ascii/er/parser.d.ts +6 -0
  105. package/dist/types/vendor/mermaid-ascii/er/types.d.ts +76 -0
  106. package/dist/types/vendor/mermaid-ascii/index.d.ts +1 -0
  107. package/dist/types/vendor/mermaid-ascii/multiline-utils.d.ts +9 -0
  108. package/dist/types/vendor/mermaid-ascii/parser.d.ts +7 -0
  109. package/dist/types/vendor/mermaid-ascii/sequence/parser.d.ts +6 -0
  110. package/dist/types/vendor/mermaid-ascii/sequence/types.d.ts +130 -0
  111. package/dist/types/vendor/mermaid-ascii/text-metrics.d.ts +21 -0
  112. package/dist/types/vendor/mermaid-ascii/types.d.ts +114 -0
  113. package/dist/types/vendor/mermaid-ascii/xychart/colors.d.ts +25 -0
  114. package/dist/types/vendor/mermaid-ascii/xychart/parser.d.ts +6 -0
  115. package/dist/types/vendor/mermaid-ascii/xychart/types.d.ts +145 -0
  116. package/dist/types/version.d.ts +18 -0
  117. package/dist/types/vterm/buffer.d.ts +99 -0
  118. package/dist/types/vterm/terminal.d.ts +44 -0
  119. package/dist/types/vterm.d.ts +8 -0
  120. package/dist/types/which.d.ts +37 -0
  121. package/dist/types/worker-host.d.ts +51 -0
  122. package/dist/types/xml.d.ts +31 -0
  123. package/package.json +59 -0
  124. package/src/abortable.ts +109 -0
  125. package/src/acp/connection.ts +344 -0
  126. package/src/acp/protocol.ts +466 -0
  127. package/src/acp/schema.ts +160 -0
  128. package/src/acp/stream.ts +82 -0
  129. package/src/acp/transport.ts +213 -0
  130. package/src/acp.ts +6 -0
  131. package/src/async.ts +91 -0
  132. package/src/binary.ts +50 -0
  133. package/src/browsers.ts +501 -0
  134. package/src/chalk.ts +312 -0
  135. package/src/cli.ts +529 -0
  136. package/src/color.ts +302 -0
  137. package/src/dates.ts +194 -0
  138. package/src/dirs.ts +1026 -0
  139. package/src/docx/converter.ts +681 -0
  140. package/src/docx/xml.ts +166 -0
  141. package/src/docx/zip.ts +87 -0
  142. package/src/docx.ts +20 -0
  143. package/src/dom/core.ts +1254 -0
  144. package/src/dom/parser.ts +370 -0
  145. package/src/dom/selector.ts +290 -0
  146. package/src/dom.ts +33 -0
  147. package/src/env.ts +400 -0
  148. package/src/fetch-retry.ts +379 -0
  149. package/src/file-lock.ts +69 -0
  150. package/src/format.ts +113 -0
  151. package/src/frontmatter.ts +205 -0
  152. package/src/fs-error.ts +56 -0
  153. package/src/glob.ts +189 -0
  154. package/src/headers.ts +167 -0
  155. package/src/index.ts +59 -0
  156. package/src/json-parse.ts +821 -0
  157. package/src/json.ts +23 -0
  158. package/src/logger/rotating-file.ts +149 -0
  159. package/src/logger.ts +798 -0
  160. package/src/loop-phase.ts +49 -0
  161. package/src/lru.ts +185 -0
  162. package/src/marked/core.ts +1576 -0
  163. package/src/marked.ts +2 -0
  164. package/src/mermaid-ascii.ts +31 -0
  165. package/src/mime.ts +159 -0
  166. package/src/module-timer.ts +148 -0
  167. package/src/path-tree.ts +147 -0
  168. package/src/path.ts +28 -0
  169. package/src/peek-file.ts +188 -0
  170. package/src/postmortem.ts +433 -0
  171. package/src/process-name.ts +57 -0
  172. package/src/procmgr.ts +247 -0
  173. package/src/prompt.ts +545 -0
  174. package/src/ptree.ts +412 -0
  175. package/src/readability/readability.ts +533 -0
  176. package/src/readability/readerable.ts +51 -0
  177. package/src/readability/types.ts +72 -0
  178. package/src/readability.ts +11 -0
  179. package/src/ring.ts +169 -0
  180. package/src/runtime-install.ts +393 -0
  181. package/src/sanitize-text.ts +96 -0
  182. package/src/snowflake.ts +121 -0
  183. package/src/stderr-guard.ts +155 -0
  184. package/src/stream.ts +454 -0
  185. package/src/tab-spacing.ts +336 -0
  186. package/src/temp.ts +134 -0
  187. package/src/template.ts +586 -0
  188. package/src/timing-buffer.ts +47 -0
  189. package/src/tls-fetch.ts +178 -0
  190. package/src/turndown/gfm.ts +106 -0
  191. package/src/turndown/html.ts +257 -0
  192. package/src/turndown/service.ts +334 -0
  193. package/src/turndown/types.ts +81 -0
  194. package/src/turndown.ts +5 -0
  195. package/src/type-guards.ts +17 -0
  196. package/src/vendor/mermaid-ascii/NOTICE +33 -0
  197. package/src/vendor/mermaid-ascii/ascii/ansi.ts +409 -0
  198. package/src/vendor/mermaid-ascii/ascii/canvas.ts +476 -0
  199. package/src/vendor/mermaid-ascii/ascii/class-diagram.ts +699 -0
  200. package/src/vendor/mermaid-ascii/ascii/converter.ts +271 -0
  201. package/src/vendor/mermaid-ascii/ascii/draw.ts +1382 -0
  202. package/src/vendor/mermaid-ascii/ascii/edge-bundling.ts +328 -0
  203. package/src/vendor/mermaid-ascii/ascii/edge-routing.ts +297 -0
  204. package/src/vendor/mermaid-ascii/ascii/er-diagram.ts +441 -0
  205. package/src/vendor/mermaid-ascii/ascii/grid.ts +578 -0
  206. package/src/vendor/mermaid-ascii/ascii/index.ts +187 -0
  207. package/src/vendor/mermaid-ascii/ascii/multiline-utils.ts +78 -0
  208. package/src/vendor/mermaid-ascii/ascii/pathfinder.ts +277 -0
  209. package/src/vendor/mermaid-ascii/ascii/sequence.ts +460 -0
  210. package/src/vendor/mermaid-ascii/ascii/shapes/circle.ts +27 -0
  211. package/src/vendor/mermaid-ascii/ascii/shapes/corners.ts +127 -0
  212. package/src/vendor/mermaid-ascii/ascii/shapes/diamond.ts +27 -0
  213. package/src/vendor/mermaid-ascii/ascii/shapes/hexagon.ts +27 -0
  214. package/src/vendor/mermaid-ascii/ascii/shapes/index.ts +101 -0
  215. package/src/vendor/mermaid-ascii/ascii/shapes/rectangle.ts +175 -0
  216. package/src/vendor/mermaid-ascii/ascii/shapes/rounded.ts +27 -0
  217. package/src/vendor/mermaid-ascii/ascii/shapes/special.ts +296 -0
  218. package/src/vendor/mermaid-ascii/ascii/shapes/stadium.ts +114 -0
  219. package/src/vendor/mermaid-ascii/ascii/shapes/state.ts +192 -0
  220. package/src/vendor/mermaid-ascii/ascii/shapes/types.ts +73 -0
  221. package/src/vendor/mermaid-ascii/ascii/types.ts +273 -0
  222. package/src/vendor/mermaid-ascii/ascii/validate.ts +120 -0
  223. package/src/vendor/mermaid-ascii/ascii/xychart.ts +875 -0
  224. package/src/vendor/mermaid-ascii/class/parser.ts +290 -0
  225. package/src/vendor/mermaid-ascii/class/types.ts +121 -0
  226. package/src/vendor/mermaid-ascii/er/parser.ts +181 -0
  227. package/src/vendor/mermaid-ascii/er/types.ts +91 -0
  228. package/src/vendor/mermaid-ascii/index.ts +14 -0
  229. package/src/vendor/mermaid-ascii/multiline-utils.ts +30 -0
  230. package/src/vendor/mermaid-ascii/parser.ts +645 -0
  231. package/src/vendor/mermaid-ascii/sequence/parser.ts +207 -0
  232. package/src/vendor/mermaid-ascii/sequence/types.ts +146 -0
  233. package/src/vendor/mermaid-ascii/text-metrics.ts +71 -0
  234. package/src/vendor/mermaid-ascii/types.ts +164 -0
  235. package/src/vendor/mermaid-ascii/xychart/colors.ts +140 -0
  236. package/src/vendor/mermaid-ascii/xychart/parser.ts +115 -0
  237. package/src/vendor/mermaid-ascii/xychart/types.ts +150 -0
  238. package/src/version.ts +99 -0
  239. package/src/vterm/buffer.ts +218 -0
  240. package/src/vterm/terminal.ts +773 -0
  241. package/src/vterm.ts +8 -0
  242. package/src/which.ts +234 -0
  243. package/src/worker-host.ts +100 -0
  244. package/src/xml.ts +313 -0
package/src/procmgr.ts ADDED
@@ -0,0 +1,247 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { Process, ProcessStatus } from "@linxiraos/pi-natives";
4
+ import type { Subprocess } from "bun";
5
+ import { getAgentDir, MAIN_CONFIG_FILENAMES } from "./dirs";
6
+ import { $env, filterChildShellEnv } from "./env";
7
+ import { $which } from "./which";
8
+
9
+ export interface ShellConfig {
10
+ shell: string;
11
+ args: string[];
12
+ env: Record<string, string>;
13
+ prefix: string | undefined;
14
+ }
15
+
16
+ /** Identifies the settings source users should edit when shell resolution fails. */
17
+ export interface ShellConfigOptions {
18
+ /** File path or runtime layer that supplied the active shell setting. */
19
+ configSource?: string;
20
+ }
21
+ let cachedShellConfig: ShellConfig | null = null;
22
+
23
+ /**
24
+ * Check if a shell binary is executable.
25
+ */
26
+ export function isExecutable(path: string): boolean {
27
+ try {
28
+ fs.accessSync(path, fs.constants.X_OK);
29
+ return true;
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Build the spawn environment (cached).
37
+ */
38
+ function buildSpawnEnv(shell: string): Record<string, string> {
39
+ const noCI = $env.PI_BASH_NO_CI || $env.CLAUDE_BASH_NO_CI;
40
+ return {
41
+ ...filterChildShellEnv(Bun.env),
42
+ SHELL: shell,
43
+ GIT_EDITOR: "true",
44
+ GPG_TTY: "not a tty",
45
+ OMPCODE: "1",
46
+ CLAUDECODE: "1",
47
+ ...(noCI ? {} : { CI: "true" }),
48
+ } as Record<string, string>;
49
+ }
50
+
51
+ /**
52
+ * Get shell args for the resolved shell.
53
+ * cmd.exe takes `/c`; PowerShell (powershell.exe / pwsh) takes
54
+ * `-NoLogo -Command`, with `-NoProfile` when PI_BASH_NO_LOGIN /
55
+ * CLAUDE_BASH_NO_LOGIN is set (profile scripts are PowerShell's login-shell
56
+ * analog); POSIX shells take `-c`, with `-l` unless the same env is set.
57
+ *
58
+ * Exported for tests; `env` overrides the process env gate.
59
+ */
60
+ export function getShellArgs(shell: string, env: Record<string, string | undefined> = $env): string[] {
61
+ if (isCmdShell(shell)) return ["/c"];
62
+ const noLogin = env.PI_BASH_NO_LOGIN || env.CLAUDE_BASH_NO_LOGIN;
63
+ if (isPowerShell(shell)) {
64
+ return noLogin ? ["-NoLogo", "-NoProfile", "-Command"] : ["-NoLogo", "-Command"];
65
+ }
66
+ return noLogin ? ["-c"] : ["-l", "-c"];
67
+ }
68
+
69
+ /** Whether the shell is Windows cmd.exe (spawn paths must use `/c`, not `-c`). */
70
+ export function isCmdShell(shell: string): boolean {
71
+ const basename = shell.replace(/\\/g, "/").split("/").pop()?.toLowerCase();
72
+ return basename === "cmd.exe" || basename === "cmd";
73
+ }
74
+
75
+ /**
76
+ * Whether the shell is Windows PowerShell or PowerShell Core (pwsh). Spawn
77
+ * paths must use `-Command`: passing the POSIX `-l -c` pair makes PowerShell
78
+ * parse `-l` as the command and fail with `The term '-l' is not recognized`.
79
+ */
80
+ export function isPowerShell(shell: string): boolean {
81
+ const basename = shell.replace(/\\/g, "/").split("/").pop()?.toLowerCase();
82
+ return basename === "powershell.exe" || basename === "powershell" || basename === "pwsh.exe" || basename === "pwsh";
83
+ }
84
+
85
+ /**
86
+ * Get shell prefix for wrapping commands (profilers, strace, etc.).
87
+ */
88
+ function getShellPrefix(): string | undefined {
89
+ return $env.PI_SHELL_PREFIX || $env.CLAUDE_CODE_SHELL_PREFIX;
90
+ }
91
+
92
+ /**
93
+ * Build full shell config from a shell path.
94
+ */
95
+ function buildConfig(shell: string): ShellConfig {
96
+ return {
97
+ shell,
98
+ args: getShellArgs(shell),
99
+ env: buildSpawnEnv(shell),
100
+ prefix: getShellPrefix(),
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Resolve a basic shell (bash or sh) as fallback.
106
+ */
107
+ export function resolveBasicShell(): string | undefined {
108
+ for (const name of ["bash", "bash.exe", "sh", "sh.exe"]) {
109
+ const resolved = $which(name);
110
+ if (resolved) return resolved;
111
+ }
112
+
113
+ if (process.platform !== "win32") {
114
+ const searchPaths = ["/bin", "/usr/bin", "/usr/local/bin", "/opt/homebrew/bin"];
115
+ const candidates = ["bash", "sh"];
116
+
117
+ for (const name of candidates) {
118
+ for (const dir of searchPaths) {
119
+ const fullPath = path.join(dir, name);
120
+ if (fs.existsSync(fullPath)) return fullPath;
121
+ }
122
+ }
123
+ }
124
+
125
+ return undefined;
126
+ }
127
+
128
+ /**
129
+ * Resolve the external shell to advertise on Windows.
130
+ *
131
+ * A host bash is OPTIONAL: bash tool commands always execute in the embedded
132
+ * brush-core shell. The resolved binary only serves the spawn-a-shell paths
133
+ * (interactive PTY sessions, ACP client terminals, SHELL env), so this
134
+ * prefers a real Git Bash when one exists and otherwise falls back to
135
+ * cmd.exe — it never fails.
136
+ *
137
+ * Search order:
138
+ * 1. Git for Windows install roots (machine + per-user installers)
139
+ * 2. scoop installs — scoop's git manifest sets GIT_INSTALL_ROOT and shims
140
+ * sh.exe/git.exe but never bash.exe, so PATH lookup alone misses it
141
+ * 3. bash.exe on PATH (Cygwin, MSYS2, ...)
142
+ * 4. sh.exe on PATH (Git for Windows' sh.exe is bash; prefer a sibling
143
+ * bash.exe when present)
144
+ * 5. cmd.exe from ComSpec
145
+ *
146
+ * Exported for tests; `env` overrides Bun.env-based discovery.
147
+ */
148
+ export function resolveWindowsShell(env: Record<string, string | undefined> = Bun.env): string {
149
+ const gitRoots = [
150
+ env.ProgramFiles && path.join(env.ProgramFiles, "Git"),
151
+ env["ProgramFiles(x86)"] && path.join(env["ProgramFiles(x86)"], "Git"),
152
+ env.LOCALAPPDATA && path.join(env.LOCALAPPDATA, "Programs", "Git"),
153
+ env.GIT_INSTALL_ROOT,
154
+ env.SCOOP && path.join(env.SCOOP, "apps", "git", "current"),
155
+ env.USERPROFILE && path.join(env.USERPROFILE, "scoop", "apps", "git", "current"),
156
+ ];
157
+ for (const root of gitRoots) {
158
+ if (!root) continue;
159
+ const candidate = path.join(root, "bin", "bash.exe");
160
+ if (fs.existsSync(candidate)) return candidate;
161
+ }
162
+
163
+ const bashOnPath = $which("bash.exe");
164
+ if (bashOnPath) return bashOnPath;
165
+
166
+ const shOnPath = $which("sh.exe");
167
+ if (shOnPath) {
168
+ const siblingBash = path.join(path.dirname(shOnPath), "bash.exe");
169
+ return fs.existsSync(siblingBash) ? siblingBash : shOnPath;
170
+ }
171
+
172
+ return env.ComSpec || env.COMSPEC || "C:\\Windows\\System32\\cmd.exe";
173
+ }
174
+
175
+ /**
176
+ * Get shell configuration based on platform.
177
+ * Resolution order:
178
+ * 1. User-specified shellPath from the active settings source
179
+ * 2. On Windows: Git Bash / bash / sh discovery, then cmd.exe (see
180
+ * {@link resolveWindowsShell}) — never fails
181
+ * 3. On Unix: $SHELL if bash/zsh, then fallback paths
182
+ * 4. Fallback: sh
183
+ */
184
+ export function getShellConfig(customShellPath?: string, options: ShellConfigOptions = {}): ShellConfig {
185
+ const configSource = options.configSource ?? path.join(getAgentDir(), MAIN_CONFIG_FILENAMES[0]);
186
+ // 1. Check user-specified shell path. Validated even on the cached path so a
187
+ // broken shellPath surfaces its guidance error instead of being masked by an
188
+ // earlier successful resolution in the same process.
189
+ if (customShellPath) {
190
+ if (!fs.existsSync(customShellPath)) {
191
+ throw new Error(`Custom shell path not found: ${customShellPath}\nPlease update shellPath in ${configSource}`);
192
+ }
193
+ if (cachedShellConfig?.shell !== customShellPath) {
194
+ cachedShellConfig = buildConfig(customShellPath);
195
+ }
196
+ return cachedShellConfig;
197
+ }
198
+ if (cachedShellConfig) {
199
+ return cachedShellConfig;
200
+ }
201
+
202
+ if (process.platform === "win32") {
203
+ cachedShellConfig = buildConfig(resolveWindowsShell());
204
+ return cachedShellConfig;
205
+ }
206
+
207
+ // Unix: prefer user's shell from $SHELL if it's bash/zsh and executable
208
+ const userShell = Bun.env.SHELL;
209
+ const isValidShell = userShell && (userShell.includes("bash") || userShell.includes("zsh"));
210
+ if (isValidShell && isExecutable(userShell)) {
211
+ cachedShellConfig = buildConfig(userShell);
212
+ return cachedShellConfig;
213
+ }
214
+
215
+ // 4. Fallback: use basic shell
216
+ const basicShell = resolveBasicShell();
217
+ if (basicShell) {
218
+ cachedShellConfig = buildConfig(basicShell);
219
+ return cachedShellConfig;
220
+ }
221
+ cachedShellConfig = buildConfig("sh");
222
+ return cachedShellConfig;
223
+ }
224
+
225
+ /**
226
+ * Check if a process is running.
227
+ */
228
+ export function isPidRunning(pid: number | Subprocess): boolean {
229
+ if (typeof pid !== "number") {
230
+ if (pid.killed) return false;
231
+ if (pid.exitCode !== null) return false;
232
+ return true;
233
+ }
234
+
235
+ return Process.fromPid(pid)?.status() === ProcessStatus.Running;
236
+ }
237
+
238
+ export async function onProcessExit(proc: Subprocess | number, abortSignal?: AbortSignal): Promise<boolean> {
239
+ if (typeof proc !== "number") {
240
+ return proc.exited.then(
241
+ () => true,
242
+ () => true,
243
+ );
244
+ }
245
+
246
+ return (await Process.fromPid(proc)?.waitForExit({ signal: abortSignal })) ?? true;
247
+ }