pzero-operator 0.1.6 → 0.1.7

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.
@@ -36,7 +36,7 @@ export function createLocalBashOperations(options) {
36
36
  return new Promise((resolve, reject) => {
37
37
  const { shell, args } = getShellConfig(options?.shellPath);
38
38
  if (!existsSync(cwd)) {
39
- reject(new Error(`Working directory does not exist: ${cwd}\nCannot execute bash commands.`));
39
+ reject(new Error(`Working directory does not exist: ${cwd}\nCannot execute shell commands.`));
40
40
  return;
41
41
  }
42
42
  const child = spawn(shell, [...args, command], {
@@ -119,6 +119,30 @@ class BashResultRenderComponent extends Container {
119
119
  function formatDuration(ms) {
120
120
  return `${(ms / 1000).toFixed(1)}s`;
121
121
  }
122
+ function getShellPromptText(shellPath) {
123
+ try {
124
+ const shellConfig = getShellConfig(shellPath);
125
+ if (process.platform === "win32") {
126
+ if (shellConfig.kind === "powershell") {
127
+ return {
128
+ description: `Execute a ${shellConfig.displayName} command in the current working directory. Returns stdout and stderr. Prefer Windows-native commands and PowerShell syntax. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,
129
+ promptSnippet: "Execute PowerShell commands on Windows (Get-ChildItem, Select-String, Test-Path, Remove-Item, etc.)",
130
+ };
131
+ }
132
+ return {
133
+ description: `Execute a shell command in the current working directory using ${shellConfig.displayName}. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,
134
+ promptSnippet: "Execute shell commands on Windows using Bash (ls, grep, find, etc.)",
135
+ };
136
+ }
137
+ }
138
+ catch {
139
+ // Fall back to the default Unix-oriented prompt text if shell discovery fails.
140
+ }
141
+ return {
142
+ description: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,
143
+ promptSnippet: "Execute bash commands (ls, grep, find, etc.)",
144
+ };
145
+ }
122
146
  function formatBashCall(args) {
123
147
  const command = str(args?.command);
124
148
  const timeout = args?.timeout;
@@ -192,11 +216,12 @@ export function createBashToolDefinition(cwd, options) {
192
216
  const ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });
193
217
  const commandPrefix = options?.commandPrefix;
194
218
  const spawnHook = options?.spawnHook;
219
+ const promptText = getShellPromptText(options?.shellPath);
195
220
  return {
196
221
  name: "bash",
197
222
  label: "terminal.exec",
198
- description: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,
199
- promptSnippet: "Execute bash commands (ls, grep, find, etc.)",
223
+ description: promptText.description,
224
+ promptSnippet: promptText.promptSnippet,
200
225
  parameters: bashSchema,
201
226
  async execute(_toolCallId, { command, timeout }, signal, onUpdate, _ctx) {
202
227
  const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
@@ -16,7 +16,7 @@ const DEFAULT_MODELS_CONFIG = {
16
16
  authHeader: true,
17
17
  compat: {
18
18
  supportsDeveloperRole: true,
19
- supportsReasoningEffort: true,
19
+ supportsReasoningEffort: false,
20
20
  },
21
21
  models: [
22
22
  {
@@ -107,6 +107,13 @@ function migrateAlemBaseUrls() {
107
107
  gptOssProvider.baseUrl = "https://llm.alem.ai";
108
108
  changed = true;
109
109
  }
110
+ if (!gptOssProvider.compat || typeof gptOssProvider.compat !== "object") {
111
+ gptOssProvider.compat = {};
112
+ }
113
+ if (gptOssProvider.compat.supportsReasoningEffort !== false) {
114
+ gptOssProvider.compat.supportsReasoningEffort = false;
115
+ changed = true;
116
+ }
110
117
  }
111
118
  for (const providerName of ["alem-ai-plus-qwen3-6", "alem-ai-plus-gemma4"]) {
112
119
  const provider = parsed.providers[providerName];
@@ -2145,7 +2145,7 @@ export class InteractiveMode {
2145
2145
  const command = isExcluded ? text.slice(2).trim() : text.slice(1).trim();
2146
2146
  if (command) {
2147
2147
  if (this.session.isBashRunning) {
2148
- this.showWarning("A bash command is already running. Press Ctrl+C to cancel it first.");
2148
+ this.showWarning("A shell command is already running. Press Ctrl+C to cancel it first.");
2149
2149
  this.editor.setText(text);
2150
2150
  return;
2151
2151
  }
@@ -4320,7 +4320,7 @@ export class InteractiveMode {
4320
4320
  if (this.bashComponent) {
4321
4321
  this.bashComponent.setComplete(undefined, false);
4322
4322
  }
4323
- this.showError(`Bash command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
4323
+ this.showError(`Shell command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
4324
4324
  }
4325
4325
  this.bashComponent = undefined;
4326
4326
  this.ui.requestRender();
@@ -2,17 +2,15 @@ import { existsSync } from "node:fs";
2
2
  import { delimiter } from "node:path";
3
3
  import { spawn, spawnSync } from "child_process";
4
4
  import { getBinDir } from "../config.js";
5
- /**
6
- * Find bash executable on PATH (cross-platform)
7
- */
8
- function findBashOnPath() {
9
- if (process.platform === "win32") {
10
- // Windows: Use 'where' and verify file exists (where can return non-existent paths)
5
+ function findExecutableOnPath(candidates) {
6
+ const commands = Array.isArray(candidates) ? candidates : [candidates];
7
+ const lookupCommand = process.platform === "win32" ? "where" : "which";
8
+ for (const candidate of commands) {
11
9
  try {
12
- const result = spawnSync("where", ["bash.exe"], { encoding: "utf-8", timeout: 5000 });
10
+ const result = spawnSync(lookupCommand, [candidate], { encoding: "utf-8", timeout: 5000 });
13
11
  if (result.status === 0 && result.stdout) {
14
12
  const firstMatch = result.stdout.trim().split(/\r?\n/)[0];
15
- if (firstMatch && existsSync(firstMatch)) {
13
+ if (firstMatch && (process.platform !== "win32" || existsSync(firstMatch))) {
16
14
  return firstMatch;
17
15
  }
18
16
  }
@@ -20,74 +18,107 @@ function findBashOnPath() {
20
18
  catch {
21
19
  // Ignore errors
22
20
  }
23
- return null;
24
21
  }
25
- // Unix: Use 'which' and trust its output (handles Termux and special filesystems)
26
- try {
27
- const result = spawnSync("which", ["bash"], { encoding: "utf-8", timeout: 5000 });
28
- if (result.status === 0 && result.stdout) {
29
- const firstMatch = result.stdout.trim().split(/\r?\n/)[0];
30
- if (firstMatch) {
31
- return firstMatch;
32
- }
22
+ return null;
23
+ }
24
+ function getShellConfigForExecutable(shellPath) {
25
+ const normalized = shellPath.toLowerCase();
26
+ if (process.platform === "win32") {
27
+ if (normalized.endsWith("pwsh.exe") || normalized.endsWith("\\pwsh")) {
28
+ return {
29
+ shell: shellPath,
30
+ args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command"],
31
+ kind: "powershell",
32
+ displayName: "PowerShell",
33
+ };
34
+ }
35
+ if (normalized.endsWith("powershell.exe") || normalized.endsWith("\\powershell")) {
36
+ return {
37
+ shell: shellPath,
38
+ args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command"],
39
+ kind: "powershell",
40
+ displayName: "Windows PowerShell",
41
+ };
33
42
  }
34
43
  }
35
- catch {
36
- // Ignore errors
37
- }
38
- return null;
44
+ return {
45
+ shell: shellPath,
46
+ args: ["-c"],
47
+ kind: "bash",
48
+ displayName: process.platform === "win32" ? "Bash" : "Shell",
49
+ };
50
+ }
51
+ /**
52
+ * Find bash executable on PATH (cross-platform)
53
+ */
54
+ function findBashOnPath() {
55
+ return process.platform === "win32" ? findExecutableOnPath("bash.exe") : findExecutableOnPath("bash");
39
56
  }
40
57
  /**
41
58
  * Resolve shell configuration based on platform and an optional explicit shell path.
42
59
  * Resolution order:
43
60
  * 1. User-specified shellPath
44
- * 2. On Windows: Git Bash in known locations, then bash on PATH
61
+ * 2. On Windows: PowerShell Core, Windows PowerShell, Git Bash, then bash on PATH
45
62
  * 3. On Unix: /bin/bash, then bash on PATH, then fallback to sh
46
63
  */
47
64
  export function getShellConfig(customShellPath) {
48
65
  // 1. Check user-specified shell path
49
66
  if (customShellPath) {
50
67
  if (existsSync(customShellPath)) {
51
- return { shell: customShellPath, args: ["-c"] };
68
+ return getShellConfigForExecutable(customShellPath);
52
69
  }
53
70
  throw new Error(`Custom shell path not found: ${customShellPath}`);
54
71
  }
55
72
  if (process.platform === "win32") {
56
- // 2. Try Git Bash in known locations
57
- const paths = [];
73
+ // 2. Prefer native PowerShell shells on Windows.
74
+ const pwshOnPath = findExecutableOnPath(["pwsh.exe", "pwsh"]);
75
+ if (pwshOnPath) {
76
+ return getShellConfigForExecutable(pwshOnPath);
77
+ }
78
+ const powershellPaths = [];
58
79
  const programFiles = process.env.ProgramFiles;
59
80
  if (programFiles) {
60
- paths.push(`${programFiles}\\Git\\bin\\bash.exe`);
81
+ powershellPaths.push(`${programFiles}\\PowerShell\\7\\pwsh.exe`);
82
+ powershellPaths.push(`${programFiles}\\Git\\bin\\bash.exe`);
61
83
  }
62
84
  const programFilesX86 = process.env["ProgramFiles(x86)"];
63
85
  if (programFilesX86) {
64
- paths.push(`${programFilesX86}\\Git\\bin\\bash.exe`);
86
+ powershellPaths.push(`${programFilesX86}\\Git\\bin\\bash.exe`);
65
87
  }
66
- for (const path of paths) {
88
+ const systemRoot = process.env.SystemRoot;
89
+ if (systemRoot) {
90
+ powershellPaths.push(`${systemRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`);
91
+ }
92
+ for (const path of powershellPaths) {
67
93
  if (existsSync(path)) {
68
- return { shell: path, args: ["-c"] };
94
+ return getShellConfigForExecutable(path);
69
95
  }
70
96
  }
97
+ const powershellOnPath = findExecutableOnPath(["powershell.exe", "powershell"]);
98
+ if (powershellOnPath) {
99
+ return getShellConfigForExecutable(powershellOnPath);
100
+ }
71
101
  // 3. Fallback: search bash.exe on PATH (Cygwin, MSYS2, WSL, etc.)
72
102
  const bashOnPath = findBashOnPath();
73
103
  if (bashOnPath) {
74
- return { shell: bashOnPath, args: ["-c"] };
104
+ return getShellConfigForExecutable(bashOnPath);
75
105
  }
76
- throw new Error(`No bash shell found. Options:\n` +
77
- ` 1. Install Git for Windows: https://git-scm.com/download/win\n` +
78
- ` 2. Add your bash to PATH (Cygwin, MSYS2, etc.)\n` +
79
- " 3. Set shellPath in settings.json\n\n" +
80
- `Searched Git Bash in:\n${paths.map((p) => ` ${p}`).join("\n")}`);
106
+ throw new Error(`No supported shell found on Windows. Options:\n` +
107
+ ` 1. Install PowerShell 7: https://github.com/PowerShell/PowerShell\n` +
108
+ ` 2. Use built-in Windows PowerShell\n` +
109
+ ` 3. Install Git for Windows: https://git-scm.com/download/win\n` +
110
+ " 4. Set shellPath in settings.json\n\n" +
111
+ `Searched common paths:\n${powershellPaths.map((p) => ` ${p}`).join("\n")}`);
81
112
  }
82
113
  // Unix: try /bin/bash, then bash on PATH, then fallback to sh
83
114
  if (existsSync("/bin/bash")) {
84
- return { shell: "/bin/bash", args: ["-c"] };
115
+ return getShellConfigForExecutable("/bin/bash");
85
116
  }
86
117
  const bashOnPath = findBashOnPath();
87
118
  if (bashOnPath) {
88
- return { shell: bashOnPath, args: ["-c"] };
119
+ return getShellConfigForExecutable(bashOnPath);
89
120
  }
90
- return { shell: "sh", args: ["-c"] };
121
+ return { shell: "sh", args: ["-c"], kind: "sh", displayName: "Shell" };
91
122
  }
92
123
  export function getShellEnv() {
93
124
  const binDir = getBinDir();
@@ -187,4 +218,4 @@ export function killProcessTree(pid) {
187
218
  }
188
219
  }
189
220
  }
190
- //# sourceMappingURL=shell.js.map
221
+ //# sourceMappingURL=shell.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pzero-operator",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Operator is a coding-first terminal AI agent from ProjectZero for software development, shell execution, local project workflows, and broader device-level operator control.",