@xynogen/pix-core 0.4.50 → 0.4.51
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/package.json +1 -1
- package/src/extension.ts +54 -0
package/package.json
CHANGED
package/src/extension.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import { createLocalBashOperations } from "@earendil-works/pi-coding-agent";
|
|
15
16
|
import registerAsk from "@xynogen/pix-ask/src/index.ts";
|
|
16
17
|
import registerBash from "@xynogen/pix-bash/src/extension.ts";
|
|
17
18
|
import registerCommands from "@xynogen/pix-commands/src/extension.ts";
|
|
@@ -81,4 +82,57 @@ export default function (pi: ExtensionAPI): void {
|
|
|
81
82
|
for (const register of MEMBERS) {
|
|
82
83
|
(register as (pi: ExtensionAPI) => void)(pi);
|
|
83
84
|
}
|
|
85
|
+
|
|
86
|
+
// ── Respect $SHELL for user ! commands ──────────────────────────────────
|
|
87
|
+
// Pi defaults to /bin/bash for ! commands. When the user's login shell
|
|
88
|
+
// differs (zsh, fish, nushell, …), redirect ! execution through $SHELL
|
|
89
|
+
// in interactive mode so aliases, functions, and rc config all work.
|
|
90
|
+
const userShell = process.env.SHELL;
|
|
91
|
+
if (userShell && !/\bbash$/.test(userShell)) {
|
|
92
|
+
const ops = createInteractiveShellOps(userShell);
|
|
93
|
+
pi.on("user_bash", () => ({ operations: ops }));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Build BashOperations that run commands through the user's real shell with
|
|
99
|
+
* rc files loaded and alias expansion enabled.
|
|
100
|
+
*
|
|
101
|
+
* Problem: `zsh -c "gss"` is non-interactive — it skips `.zshrc` and disables
|
|
102
|
+
* alias expansion. Even `zsh -c ". ~/.zshrc; gss"` fails because aliases
|
|
103
|
+
* are resolved at parse-time, before `.zshrc` is sourced.
|
|
104
|
+
*
|
|
105
|
+
* Solution: source the rc file, then `eval` the command. `eval` creates a
|
|
106
|
+
* new parse boundary, so aliases defined by the source are available.
|
|
107
|
+
*/
|
|
108
|
+
function createInteractiveShellOps(
|
|
109
|
+
shellPath: string,
|
|
110
|
+
): import("@earendil-works/pi-coding-agent").BashOperations {
|
|
111
|
+
const inner = createLocalBashOperations({ shellPath });
|
|
112
|
+
const name = shellPath.split("/").pop() ?? "";
|
|
113
|
+
const home = process.env.HOME ?? "~";
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
exec(command, cwd, options) {
|
|
117
|
+
return inner.exec(wrapForShell(name, home, command), cwd, options);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Wrap a command so it runs inside the user's shell with rc and alias support.
|
|
124
|
+
*/
|
|
125
|
+
function wrapForShell(shell: string, home: string, command: string): string {
|
|
126
|
+
if (shell === "zsh") {
|
|
127
|
+
// 1. source .zshrc for aliases/functions/PATH
|
|
128
|
+
// 2. eval to create a fresh parse boundary so aliases expand
|
|
129
|
+
return `. ${home}/.zshrc 2>/dev/null; eval ${escapeForShell(command)}`;
|
|
130
|
+
}
|
|
131
|
+
// Other shells: run as-is, rely on shellPath alone
|
|
132
|
+
return command;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Single-quote a string for safe shell embedding. */
|
|
136
|
+
function escapeForShell(s: string): string {
|
|
137
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
84
138
|
}
|