pi-pwsh-native 0.1.2 → 0.1.3

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to this project will be documented here.
4
4
 
5
+ ## 0.1.3 — 2026-08-01
6
+
7
+ ### Fixed
8
+
9
+ - Respect the final PowerShell command's success state instead of allowing a stale native `$LASTEXITCODE` to override it.
10
+
11
+ ### Changed
12
+
13
+ - Reduced recurring model prompt overhead by shortening the shell description and consolidating PowerShell guidance.
14
+ - Deferred PowerShell runtime probing until a session starts, avoiding unnecessary subprocesses in Pi invocations that only load extensions.
15
+ - Declared the supported Pi and Node.js version ranges explicitly.
16
+ - Added minimum/latest Pi compatibility coverage and prompt-budget regression tests.
17
+
5
18
  ## 0.1.2 — 2026-07-30
6
19
 
7
20
  ### Added
package/README.md CHANGED
@@ -47,7 +47,8 @@ Choose `pi-pwsh-native` when you want a small, auditable PowerShell 7 replacemen
47
47
 
48
48
  - Windows
49
49
  - PowerShell 7+
50
- - Pi with extension support
50
+ - Node.js 22.19 or newer
51
+ - Pi 0.83.0 or newer (before 1.0)
51
52
 
52
53
  The initial implementation is developed and tested against:
53
54
 
@@ -59,7 +60,7 @@ The initial implementation is developed and tested against:
59
60
  Install the pinned npm release:
60
61
 
61
62
  ```powershell
62
- pi install npm:pi-pwsh-native@0.1.2
63
+ pi install npm:pi-pwsh-native@0.1.3
63
64
  ```
64
65
 
65
66
  Alternatively, install the public GitHub package directly:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-pwsh-native",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Native PowerShell 7 tooling for the Pi coding agent on Windows",
5
5
  "license": "MIT",
6
6
  "author": "Tako (https://github.com/takomine)",
@@ -20,7 +20,7 @@
20
20
  "win32"
21
21
  ],
22
22
  "engines": {
23
- "node": ">=20"
23
+ "node": ">=22.19.0"
24
24
  },
25
25
  "keywords": [
26
26
  "pi-package",
@@ -55,7 +55,7 @@
55
55
  "smoke": "npm run test:runtime && npm run test:exitcode && npm run test:timeout"
56
56
  },
57
57
  "peerDependencies": {
58
- "@earendil-works/pi-coding-agent": "*"
58
+ "@earendil-works/pi-coding-agent": ">=0.83.0 <1.0.0"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@earendil-works/pi-coding-agent": "^0.83.0",
package/src/index.ts CHANGED
@@ -27,11 +27,17 @@ export function selectActiveTools(
27
27
  return [...new Set(selected)];
28
28
  }
29
29
 
30
- function toolDescription(runtime: PowerShellRuntime, config: PwshNativeConfig): string {
31
- const elevation = config.elevationGuidance
32
- ? " Elevation guidance is enabled, but elevate only after an explicit user request."
33
- : " Do not elevate unless the user explicitly requests it.";
34
- return `Execute a native PowerShell 7 command on Windows with PowerShell ${runtime.version} (${runtime.executable}). Returns stdout and stderr. Output is truncated to the last 2000 lines or 50KB, whichever is reached first; full truncated output is saved to a temporary file. Commands have no default timeout; set timeout in seconds when needed.${elevation}`;
30
+ export const PWSH_TOOL_DESCRIPTION =
31
+ "Execute native PowerShell 7 on Windows. Output is tail-truncated to 2,000 lines or 50 KB; full output is saved to a temporary file. Optional timeout is in seconds.";
32
+
33
+ export function createPromptGuidelines(config: PwshNativeConfig): string[] {
34
+ return [
35
+ "Use pwsh only when a shell is needed; use native PowerShell syntax and $env:NAME for environment variables.",
36
+ "Prefer dedicated file and search tools over pwsh; otherwise bound recursive output and prefer rg or fd when available.",
37
+ config.elevationGuidance
38
+ ? "In pwsh, elevation guidance is enabled, but elevate only after an explicit user request."
39
+ : "Do not elevate from pwsh unless the user explicitly requests it.",
40
+ ];
35
41
  }
36
42
 
37
43
  function unavailableUserCommand(reason: string) {
@@ -55,51 +61,51 @@ export default function piPwshNative(pi: ExtensionAPI): void {
55
61
 
56
62
  let config: PwshNativeConfig | undefined;
57
63
  let runtime: PowerShellRuntime | undefined;
64
+ let operations: ReturnType<typeof createPowerShellOperations> | undefined;
58
65
  let setupError: string | undefined;
59
66
  try {
60
67
  config = loadConfig({ agentDir: getAgentDir() }).config;
61
- runtime = resolvePowerShellRuntime(config);
62
68
  } catch (error) {
63
69
  setupError = errorMessage(error);
64
70
  }
65
71
 
66
- if (setupError || !config || !runtime) {
67
- const reason = setupError ?? "unknown setup failure";
68
- pi.on("session_start", (_event, ctx) => {
72
+ pi.on("session_start", (_event, ctx) => {
73
+ if (!setupError && config && !runtime) {
74
+ try {
75
+ runtime = resolvePowerShellRuntime(config);
76
+ operations = createPowerShellOperations(runtime, config);
77
+ } catch (error) {
78
+ setupError = errorMessage(error);
79
+ }
80
+ }
81
+
82
+ if (setupError || !config || !runtime || !operations) {
83
+ const reason = setupError ?? "unknown setup failure";
69
84
  pi.setActiveTools(selectActiveTools(pi.getActiveTools(), true, false));
70
85
  ctx.ui.notify(`pi-pwsh-native: ${reason} Bash was disabled; no shell fallback was activated.`, "error");
71
- });
72
- pi.on("user_bash", () => unavailableUserCommand(reason));
73
- return;
74
- }
75
-
76
- const resolvedConfig = config;
77
- const resolvedRuntime = runtime;
78
- const operations = createPowerShellOperations(resolvedRuntime, resolvedConfig);
86
+ return;
87
+ }
79
88
 
80
- pi.on("session_start", (_event, ctx) => {
81
89
  const definition = createBashToolDefinition(ctx.cwd, { operations });
82
90
  pi.registerTool({
83
91
  ...definition,
84
92
  name: "pwsh",
85
93
  label: "pwsh",
86
- description: toolDescription(resolvedRuntime, resolvedConfig),
87
- promptSnippet: "Execute native PowerShell 7 commands on Windows",
88
- promptGuidelines: [
89
- "Use pwsh only when a shell is needed; write native PowerShell syntax and avoid Bash-only syntax or implicit Bash translation.",
90
- "Prefer read, write, edit, ls, find, and grep over pwsh when a dedicated tool can complete the task more directly.",
91
- "In pwsh, use $env:NAME for environment variables; single quotes are literal, double quotes interpolate, and the backtick is the escape character.",
92
- "Prefer bounded rg or fd commands for repository search; tightly bound recursive Get-ChildItem and Select-String operations.",
93
- "Do not elevate pwsh commands unless the user explicitly requests elevation.",
94
- "Inspect PI_* environment variables in pwsh when current Pi model or session metadata is needed.",
95
- ],
94
+ description: PWSH_TOOL_DESCRIPTION,
95
+ promptSnippet: "Execute native PowerShell 7 commands",
96
+ promptGuidelines: createPromptGuidelines(config),
96
97
  });
97
98
  pi.setActiveTools(
98
- selectActiveTools(pi.getActiveTools(), resolvedConfig.preserveDiscoveryTools, true),
99
+ selectActiveTools(pi.getActiveTools(), config.preserveDiscoveryTools, true),
99
100
  );
100
101
  });
101
102
 
102
- if (resolvedConfig.replaceUserBash) {
103
- pi.on("user_bash", () => ({ operations }));
103
+ const replaceUserBash = config?.replaceUserBash ?? true;
104
+ if (replaceUserBash) {
105
+ pi.on("user_bash", () =>
106
+ operations
107
+ ? { operations }
108
+ : unavailableUserCommand(setupError ?? "PowerShell has not been initialized"),
109
+ );
104
110
  }
105
111
  }
@@ -10,15 +10,16 @@ export const UTF8_PREFIX = [
10
10
  ].join("; ") + "; ";
11
11
 
12
12
  /**
13
- * Preserve a native program's real exit code. If no native program set
14
- * LASTEXITCODE, map PowerShell's success state to 0/1.
13
+ * Report the final command's status. A successful final command wins over a
14
+ * stale LASTEXITCODE; a failed final native command retains its real code;
15
+ * other PowerShell failures map to 1.
15
16
  *
16
17
  * The leading newline detaches the epilogue from a trailing line comment. The
17
18
  * leading semicolon also prevents a trailing backtick from joining the
18
19
  * epilogue to the user's final command.
19
20
  */
20
21
  export const EXIT_EPILOGUE =
21
- "\n; $__pi_ok = $?; if ($null -ne $global:LASTEXITCODE) { exit $global:LASTEXITCODE } else { exit ($__pi_ok ? 0 : 1) }";
22
+ "\n; $__pi_ok = $?; if ($__pi_ok) { exit 0 } elseif ($null -ne $global:LASTEXITCODE -and $global:LASTEXITCODE -ne 0) { exit $global:LASTEXITCODE } else { exit 1 }";
22
23
 
23
24
  export function buildPowerShellSource(command: string, config: PwshNativeConfig): string {
24
25
  const strict = config.strictMode ? "$ErrorActionPreference = 'Stop'; " : "";