@simon_he/pi 0.2.9 → 0.2.10

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/README.md CHANGED
@@ -125,15 +125,21 @@ eval "$(prun --init bash)"
125
125
 
126
126
  # fish
127
127
  eval (prun --init fish)
128
+
129
+ # Windows PowerShell
130
+ prun --init powershell | Out-String | Invoke-Expression
131
+
132
+ # PowerShell 7+
133
+ prun --init pwsh | Out-String | Invoke-Expression
128
134
  ```
129
135
 
130
- > Note: This lets the command selected by prun be available immediately in your shell history (press ↑ to recall).
136
+ > Note: This lets the command selected by `prun` or `pfind` be available immediately in your shell history (press ↑ to recall).
131
137
 
132
138
  Auto integration (built-in):
133
139
 
134
- - In interactive shells, the first `prun` run will append the right line to your shell rc (zsh: `~/.zshrc`, bash: `~/.bashrc`, fish: `~/.config/fish/config.fish`).
140
+ - In interactive shells, the first `prun` run will append the right line to your shell rc/profile (zsh: `~/.zshrc`, bash: `~/.bashrc`, fish: `~/.config/fish/config.fish`, PowerShell: `$PROFILE`).
135
141
  - Disable with `PI_NO_AUTO_INIT=1` (or set `PI_AUTO_INIT=0`).
136
- - Open a new terminal (or source the rc file) after the first run.
142
+ - Open a new terminal (or source the rc/profile) after the first run.
137
143
 
138
144
  Make it persistent:
139
145
 
@@ -146,6 +152,12 @@ echo 'eval "$(prun --init bash)"' >> ~/.bashrc
146
152
 
147
153
  # fish
148
154
  echo 'prun --init fish | source' >> ~/.config/fish/config.fish
155
+
156
+ # Windows PowerShell
157
+ Add-Content -Path $PROFILE -Value 'prun --init powershell | Out-String | Invoke-Expression'
158
+
159
+ # PowerShell 7+
160
+ Add-Content -Path $PROFILE -Value 'prun --init pwsh | Out-String | Invoke-Expression'
149
161
  ```
150
162
 
151
163
  Reload your shell config (or open a new terminal) after adding the line.
package/dist/index.cjs CHANGED
@@ -47,7 +47,7 @@ let node_module = require("node:module");
47
47
  let node_url = require("node:url");
48
48
 
49
49
  //#region package.json
50
- var version = "0.2.9";
50
+ var version = "0.2.10";
51
51
 
52
52
  //#endregion
53
53
  //#region src/tty.ts
@@ -956,16 +956,419 @@ function getCcommand() {
956
956
  }
957
957
 
958
958
  //#endregion
959
- //#region src/pfind.ts
959
+ //#region src/prun.ts
960
+ async function prun(params) {
961
+ ensurePrunAutoInit();
962
+ const hadNoHistoryEnv = node_process.default.env.CCOMMAND_NO_HISTORY != null || node_process.default.env.NO_HISTORY != null;
963
+ const initialNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY ?? node_process.default.env.NO_HISTORY;
964
+ const prevNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY;
965
+ if (!(hadNoHistoryEnv && isNoHistory$1(initialNoHistory))) delete node_process.default.env.CCOMMAND_NO_HISTORY;
966
+ else node_process.default.env.CCOMMAND_NO_HISTORY = "1";
967
+ const { ccommand } = getCcommand();
968
+ try {
969
+ await ccommand(params);
970
+ } finally {
971
+ if (prevNoHistory == null) delete node_process.default.env.CCOMMAND_NO_HISTORY;
972
+ else node_process.default.env.CCOMMAND_NO_HISTORY = prevNoHistory;
973
+ }
974
+ }
975
+ const isZh$2 = node_process.default.env.PI_Lang === "zh";
976
+ const safeShellValue = /^[\w./:@%+=,-]+$/;
960
977
  function isNoHistory$1(value) {
961
978
  if (!value) return false;
962
979
  const normalized = value.toLowerCase();
963
980
  return normalized === "1" || normalized === "true" || normalized === "yes";
964
981
  }
982
+ function shellQuote(value) {
983
+ if (value === "") return "''";
984
+ if (safeShellValue.test(value)) return value;
985
+ return `'${value.replace(/'/g, `'\\''`)}'`;
986
+ }
987
+ function powerShellQuote(value) {
988
+ if (value === "") return "''";
989
+ return `'${value.replace(/'/g, "''")}'`;
990
+ }
991
+ function splitCommand(value) {
992
+ const parts = [];
993
+ let current = "";
994
+ let quote = null;
995
+ let hasValue = false;
996
+ const pushCurrent = () => {
997
+ if (!hasValue) return;
998
+ parts.push(current);
999
+ current = "";
1000
+ hasValue = false;
1001
+ };
1002
+ for (let i = 0; i < value.length; i++) {
1003
+ const char = value[i];
1004
+ if (quote) {
1005
+ if (char === quote) {
1006
+ quote = null;
1007
+ hasValue = true;
1008
+ continue;
1009
+ }
1010
+ if (quote === "\"" && char === "\\") {
1011
+ const next = value[i + 1];
1012
+ if (next) {
1013
+ current += next;
1014
+ hasValue = true;
1015
+ i++;
1016
+ continue;
1017
+ }
1018
+ }
1019
+ current += char;
1020
+ hasValue = true;
1021
+ continue;
1022
+ }
1023
+ if (char === "\"" || char === "'") {
1024
+ quote = char;
1025
+ hasValue = true;
1026
+ continue;
1027
+ }
1028
+ if (/\s/.test(char)) {
1029
+ pushCurrent();
1030
+ while (i + 1 < value.length && /\s/.test(value[i + 1])) i++;
1031
+ continue;
1032
+ }
1033
+ if (char === "\\") {
1034
+ const next = value[i + 1];
1035
+ if (next) {
1036
+ current += next;
1037
+ hasValue = true;
1038
+ i++;
1039
+ continue;
1040
+ }
1041
+ }
1042
+ current += char;
1043
+ hasValue = true;
1044
+ }
1045
+ pushCurrent();
1046
+ return parts;
1047
+ }
1048
+ function normalizeShellName(value) {
1049
+ const shell = (value || "").toLowerCase().replace(/\.exe$/, "");
1050
+ if (shell === "powershell") return "powershell";
1051
+ if (shell === "pwsh") return "pwsh";
1052
+ if (shell === "fish" || shell === "zsh" || shell === "bash") return shell;
1053
+ return shell;
1054
+ }
1055
+ function detectShell() {
1056
+ const envShell = normalizeShellName(node_path.default.basename(node_process.default.env.SHELL || ""));
1057
+ if (node_process.default.env.FISH_VERSION) return "fish";
1058
+ if (node_process.default.env.ZSH_VERSION) return "zsh";
1059
+ if (node_process.default.env.BASH_VERSION) return "bash";
1060
+ if (node_process.default.env.POWERSHELL_DISTRIBUTION_CHANNEL) return "pwsh";
1061
+ if (envShell) return envShell;
1062
+ if (node_process.default.platform === "win32") return "powershell";
1063
+ return "zsh";
1064
+ }
1065
+ function getPowerShellProfilePath(shell, home) {
1066
+ if (node_process.default.platform === "win32") {
1067
+ const documentsHome = node_process.default.env.USERPROFILE || home;
1068
+ const profileDir = shell === "pwsh" ? "PowerShell" : "WindowsPowerShell";
1069
+ return node_path.default.join(documentsHome, "Documents", profileDir, "Microsoft.PowerShell_profile.ps1");
1070
+ }
1071
+ const configHome = node_process.default.env.XDG_CONFIG_HOME || node_path.default.join(home, ".config");
1072
+ return node_path.default.join(configHome, "powershell", "Microsoft.PowerShell_profile.ps1");
1073
+ }
1074
+ function ensurePrunAutoInit() {
1075
+ if (!shouldAutoInit()) return;
1076
+ const shell = detectShell();
1077
+ const home = node_process.default.env.HOME || node_os.default.homedir();
1078
+ let rcFile = "";
1079
+ let initLine = "";
1080
+ if (shell === "zsh") {
1081
+ const zdotdir = node_process.default.env.ZDOTDIR || home;
1082
+ rcFile = node_path.default.join(zdotdir, ".zshrc");
1083
+ initLine = "eval \"$(prun --init zsh)\"";
1084
+ } else if (shell === "bash") {
1085
+ rcFile = node_path.default.join(home, ".bashrc");
1086
+ initLine = "eval \"$(prun --init bash)\"";
1087
+ } else if (shell === "fish") {
1088
+ const configHome = node_process.default.env.XDG_CONFIG_HOME || node_path.default.join(home, ".config");
1089
+ rcFile = node_path.default.join(configHome, "fish", "config.fish");
1090
+ initLine = "prun --init fish | source";
1091
+ } else if (shell === "powershell" || shell === "pwsh") {
1092
+ rcFile = getPowerShellProfilePath(shell, home);
1093
+ initLine = `prun --init ${shell} | Out-String | Invoke-Expression`;
1094
+ } else return;
1095
+ try {
1096
+ const dir = node_path.default.dirname(rcFile);
1097
+ if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
1098
+ const content = node_fs.default.existsSync(rcFile) ? node_fs.default.readFileSync(rcFile, "utf8") : "";
1099
+ if (!/prun\s+--init/.test(content)) {
1100
+ const prefix = content.length && !content.endsWith("\n") ? "\n" : "";
1101
+ node_fs.default.appendFileSync(rcFile, `${prefix}${initLine}\n`, "utf8");
1102
+ }
1103
+ } catch {}
1104
+ }
1105
+ function shouldAutoInit() {
1106
+ const auto = node_process.default.env.PI_AUTO_INIT || node_process.default.env.PRUN_AUTO_INIT;
1107
+ if (auto != null) return isNoHistory$1(auto);
1108
+ if (isNoHistory$1(node_process.default.env.PI_NO_AUTO_INIT || node_process.default.env.PRUN_NO_AUTO_INIT)) return false;
1109
+ if (node_process.default.env.CI) return false;
1110
+ if (!node_process.default.stdout.isTTY || !node_process.default.stdin.isTTY) return false;
1111
+ return true;
1112
+ }
1113
+ function printPrunInit(args = []) {
1114
+ const shellArg = normalizeShellName(args[0]);
1115
+ const binArg = args[1] || node_process.default.env.PRUN_BIN || "prun";
1116
+ const bin = shellQuote(binArg);
1117
+ const shell = shellArg || detectShell() || "zsh";
1118
+ const historyHintExpr = "${CCOMMAND_HISTORY_HINT:-${XDG_CACHE_HOME:-$HOME/.cache}/ccommand/last-history}";
1119
+ let script = "";
1120
+ if (shell === "zsh") script = [
1121
+ "prun() {",
1122
+ ` local bin=${bin}`,
1123
+ " local -a cmd",
1124
+ " cmd=(${=bin})",
1125
+ " command \"${cmd[@]}\" \"$@\"",
1126
+ "}",
1127
+ "__prun_sync_history() {",
1128
+ " local history_disable=${CCOMMAND_NO_HISTORY:-${NO_HISTORY:-\"\"}}",
1129
+ " local history_disable_lower=${history_disable:l}",
1130
+ " if [[ $history_disable_lower == \"1\" || $history_disable_lower == \"true\" || $history_disable_lower == \"yes\" ]]; then",
1131
+ " return",
1132
+ " fi",
1133
+ ` local history_hint=${historyHintExpr}`,
1134
+ " if [[ ! -f $history_hint ]]; then",
1135
+ " return",
1136
+ " fi",
1137
+ " local line",
1138
+ " line=$(<\"$history_hint\")",
1139
+ " local hint_ts=${line%%$'\\t'*}",
1140
+ " local hint_cmd=${line#*$'\\t'}",
1141
+ " if [[ -z $hint_ts || $hint_ts == $line ]]; then",
1142
+ " hint_cmd=$line",
1143
+ " hint_ts=\"\"",
1144
+ " fi",
1145
+ " if [[ -n $hint_ts && $hint_ts == ${__PRUN_HISTORY_HINT_TS:-\"\"} ]]; then",
1146
+ " return",
1147
+ " fi",
1148
+ " __PRUN_HISTORY_HINT_TS=$hint_ts",
1149
+ " fc -R",
1150
+ " if [[ $hint_cmd != pfind* && $hint_cmd != prun* ]]; then",
1151
+ " return",
1152
+ " fi",
1153
+ " local last_line",
1154
+ " last_line=$(fc -l -1 2>/dev/null)",
1155
+ " local last_cmd",
1156
+ " last_cmd=$(printf \"%s\" \"$last_line\" | sed -E \"s/^[[:space:]]*[0-9]+[[:space:]]*//\")",
1157
+ " if [[ $last_cmd == \"$hint_cmd\" ]]; then",
1158
+ " return",
1159
+ " fi",
1160
+ " if [[ $last_cmd == prun || $last_cmd == prun\\ * ]]; then",
1161
+ " local last_num",
1162
+ " last_num=$(printf \"%s\" \"$last_line\" | sed -E \"s/^[[:space:]]*([0-9]+).*/\\1/\")",
1163
+ " if [[ -n $last_num ]]; then",
1164
+ " history -d $last_num 2>/dev/null",
1165
+ " fi",
1166
+ " fi",
1167
+ " print -s -- \"$hint_cmd\"",
1168
+ "}",
1169
+ "",
1170
+ "if ! typeset -f __prun_precmd >/dev/null; then",
1171
+ " __prun_precmd() { __prun_sync_history }",
1172
+ " autoload -Uz add-zsh-hook",
1173
+ " add-zsh-hook precmd __prun_precmd",
1174
+ "fi"
1175
+ ].join("\n");
1176
+ else if (shell === "bash") script = [
1177
+ "prun() {",
1178
+ ` local bin=${bin}`,
1179
+ " local -a cmd",
1180
+ " read -r -a cmd <<< \"$bin\"",
1181
+ " command \"${cmd[@]}\" \"$@\"",
1182
+ "}",
1183
+ "__prun_sync_history() {",
1184
+ " local history_disable=${CCOMMAND_NO_HISTORY:-${NO_HISTORY:-\"\"}}",
1185
+ " local history_disable_lower",
1186
+ " history_disable_lower=$(printf '%s' \"$history_disable\" | tr '[:upper:]' '[:lower:]')",
1187
+ " if [[ $history_disable_lower == \"1\" || $history_disable_lower == \"true\" || $history_disable_lower == \"yes\" ]]; then",
1188
+ " return",
1189
+ " fi",
1190
+ ` local history_hint=${historyHintExpr}`,
1191
+ " if [[ ! -f $history_hint ]]; then",
1192
+ " return",
1193
+ " fi",
1194
+ " local line",
1195
+ " line=$(<\"$history_hint\")",
1196
+ " local hint_ts=\"${line%%$'\\t'*}\"",
1197
+ " local hint_cmd=\"${line#*$'\\t'}\"",
1198
+ " if [[ -z $hint_ts || $hint_ts == \"$line\" ]]; then",
1199
+ " hint_cmd=\"$line\"",
1200
+ " hint_ts=\"\"",
1201
+ " fi",
1202
+ " if [[ -n $hint_ts && $hint_ts == \"${__PRUN_HISTORY_HINT_TS:-}\" ]]; then",
1203
+ " return",
1204
+ " fi",
1205
+ " __PRUN_HISTORY_HINT_TS=$hint_ts",
1206
+ " if [[ $hint_cmd != pfind* && $hint_cmd != prun* ]]; then",
1207
+ " return",
1208
+ " fi",
1209
+ " history -n",
1210
+ " local last_line",
1211
+ " last_line=$(history 1)",
1212
+ " local last_cmd",
1213
+ " last_cmd=$(printf \"%s\" \"$last_line\" | sed -E \"s/^[[:space:]]*[0-9]+[[:space:]]*//\")",
1214
+ " if [[ $last_cmd == \"$hint_cmd\" ]]; then",
1215
+ " return",
1216
+ " fi",
1217
+ " if [[ $last_cmd == prun || $last_cmd == prun\\ * ]]; then",
1218
+ " local last_num",
1219
+ " last_num=$(printf \"%s\" \"$last_line\" | sed -E \"s/^[[:space:]]*([0-9]+).*/\\1/\")",
1220
+ " if [[ -n $last_num ]]; then",
1221
+ " history -d \"$last_num\" 2>/dev/null",
1222
+ " fi",
1223
+ " fi",
1224
+ " history -s -- \"$hint_cmd\"",
1225
+ "}",
1226
+ "",
1227
+ "if [[ -z \"${__PRUN_PROMPT_INSTALLED:-}\" ]]; then",
1228
+ " __PRUN_PROMPT_INSTALLED=1",
1229
+ " if [[ -n \"${PROMPT_COMMAND:-}\" ]]; then",
1230
+ " PROMPT_COMMAND=\"__prun_sync_history;${PROMPT_COMMAND}\"",
1231
+ " else",
1232
+ " PROMPT_COMMAND=\"__prun_sync_history\"",
1233
+ " fi",
1234
+ "fi"
1235
+ ].join("\n");
1236
+ else if (shell === "fish") script = [
1237
+ "function prun",
1238
+ ` set -l bin ${bin}`,
1239
+ " set -l cmd (string split -- \" \" $bin)",
1240
+ " command $cmd $argv",
1241
+ " set -l history_disable $CCOMMAND_NO_HISTORY",
1242
+ " if test -z \"$history_disable\"",
1243
+ " set history_disable $NO_HISTORY",
1244
+ " end",
1245
+ " set history_disable (string lower -- (string trim -- \"$history_disable\"))",
1246
+ " if test \"$history_disable\" != \"1\" -a \"$history_disable\" != \"true\" -a \"$history_disable\" != \"yes\"",
1247
+ " history --merge",
1248
+ " set -l history_hint $CCOMMAND_HISTORY_HINT",
1249
+ " if test -z \"$history_hint\"",
1250
+ " set -l cache_home $XDG_CACHE_HOME",
1251
+ " if test -z \"$cache_home\"",
1252
+ " set cache_home \"$HOME/.cache\"",
1253
+ " end",
1254
+ " set history_hint \"$cache_home/ccommand/last-history\"",
1255
+ " end",
1256
+ " if test -f \"$history_hint\"",
1257
+ " set -l last_cmd (string trim -- (cat \"$history_hint\"))",
1258
+ " set -l last_cmd (string replace -r \"^[0-9]+\\t\" \"\" -- \"$last_cmd\")",
1259
+ " if string match -q \"pfind*\" -- \"$last_cmd\"; or string match -q \"prun*\" -- \"$last_cmd\"",
1260
+ " set -l last_hist (history --max=1)",
1261
+ " if test \"$last_hist\" != \"$last_cmd\"",
1262
+ " history add -- \"$last_cmd\"",
1263
+ " end",
1264
+ " end",
1265
+ " end",
1266
+ " end",
1267
+ "end"
1268
+ ].join("\n");
1269
+ else if (shell === "powershell" || shell === "pwsh") script = [
1270
+ `$script:__prun_bin = @(${splitCommand(binArg).map(powerShellQuote).join(", ") || powerShellQuote("prun")})`,
1271
+ "",
1272
+ "function global:prun {",
1273
+ " param([Parameter(ValueFromRemainingArguments = $true)] [string[]] $CliArgs)",
1274
+ " $command = $script:__prun_bin[0]",
1275
+ " if ($command -ieq \"prun\") {",
1276
+ " $resolved = Get-Command -Name $command -CommandType Application,ExternalScript -ErrorAction SilentlyContinue | Select-Object -First 1",
1277
+ " if ($null -ne $resolved) {",
1278
+ " if ($resolved.Path) { $command = $resolved.Path }",
1279
+ " elseif ($resolved.Definition) { $command = $resolved.Definition }",
1280
+ " elseif ($resolved.Source) { $command = $resolved.Source }",
1281
+ " }",
1282
+ " }",
1283
+ " $extra = @()",
1284
+ " if ($script:__prun_bin.Count -gt 1) {",
1285
+ " $extra = $script:__prun_bin[1..($script:__prun_bin.Count - 1)]",
1286
+ " }",
1287
+ " & $command @extra @CliArgs",
1288
+ "}",
1289
+ "",
1290
+ "function global:__prun_sync_history {",
1291
+ " $historyDisable = $env:CCOMMAND_NO_HISTORY",
1292
+ " if ([string]::IsNullOrWhiteSpace($historyDisable)) {",
1293
+ " $historyDisable = $env:NO_HISTORY",
1294
+ " }",
1295
+ " $historyDisable = \"$historyDisable\".Trim().ToLowerInvariant()",
1296
+ " if ($historyDisable -eq \"1\" -or $historyDisable -eq \"true\" -or $historyDisable -eq \"yes\") {",
1297
+ " return",
1298
+ " }",
1299
+ " $historyHint = $env:CCOMMAND_HISTORY_HINT",
1300
+ " if ([string]::IsNullOrWhiteSpace($historyHint)) {",
1301
+ " $cacheHome = $env:XDG_CACHE_HOME",
1302
+ " if ([string]::IsNullOrWhiteSpace($cacheHome)) {",
1303
+ " $cacheHome = Join-Path $HOME \".cache\"",
1304
+ " }",
1305
+ " $historyHint = Join-Path (Join-Path $cacheHome \"ccommand\") \"last-history\"",
1306
+ " }",
1307
+ " if (-not (Test-Path -LiteralPath $historyHint)) {",
1308
+ " return",
1309
+ " }",
1310
+ " $line = (Get-Content -LiteralPath $historyHint -Raw -ErrorAction SilentlyContinue)",
1311
+ " if ([string]::IsNullOrWhiteSpace($line)) {",
1312
+ " return",
1313
+ " }",
1314
+ " $line = $line.Trim()",
1315
+ " $hintTs = \"\"",
1316
+ " $hintCmd = $line",
1317
+ " $parts = $line -split \"`t\", 2",
1318
+ " if ($parts.Count -eq 2 -and $parts[0] -match \"^\\d+$\") {",
1319
+ " $hintTs = $parts[0]",
1320
+ " $hintCmd = $parts[1]",
1321
+ " }",
1322
+ " if (-not $hintCmd.StartsWith(\"prun\") -and -not $hintCmd.StartsWith(\"pfind\")) {",
1323
+ " return",
1324
+ " }",
1325
+ " if ($hintTs -and $script:__PRUN_HISTORY_HINT_TS -eq $hintTs) {",
1326
+ " return",
1327
+ " }",
1328
+ " $script:__PRUN_HISTORY_HINT_TS = $hintTs",
1329
+ " $psReadLineType = \"Microsoft.PowerShell.PSConsoleReadLine\" -as [type]",
1330
+ " if ($null -eq $psReadLineType) {",
1331
+ " return",
1332
+ " }",
1333
+ " $psReadLineType::AddToHistory($hintCmd)",
1334
+ "}",
1335
+ "",
1336
+ "if (-not $script:__PRUN_PROMPT_INSTALLED) {",
1337
+ " $script:__PRUN_PROMPT_INSTALLED = $true",
1338
+ " if (-not $script:__prun_original_prompt) {",
1339
+ " if (Test-Path Function:\\prompt) {",
1340
+ " $script:__prun_original_prompt = $function:prompt",
1341
+ " }",
1342
+ " }",
1343
+ " function global:prompt {",
1344
+ " __prun_sync_history",
1345
+ " if ($script:__prun_original_prompt) {",
1346
+ " & $script:__prun_original_prompt",
1347
+ " return",
1348
+ " }",
1349
+ " \"PS $($executionContext.SessionState.Path.CurrentLocation)> \"",
1350
+ " }",
1351
+ "}"
1352
+ ].join("\n");
1353
+ else {
1354
+ console.log(picocolors.default.red(isZh$2 ? `不支持的 shell: ${shell}` : `Unsupported shell: ${shell}`));
1355
+ return;
1356
+ }
1357
+ console.log(script);
1358
+ }
1359
+
1360
+ //#endregion
1361
+ //#region src/pfind.ts
1362
+ function isNoHistory(value) {
1363
+ if (!value) return false;
1364
+ const normalized = value.toLowerCase();
1365
+ return normalized === "1" || normalized === "true" || normalized === "yes";
1366
+ }
965
1367
  async function pfind(params) {
1368
+ ensurePrunAutoInit();
966
1369
  const hadNoHistoryEnv = node_process.default.env.CCOMMAND_NO_HISTORY != null || node_process.default.env.NO_HISTORY != null;
967
1370
  const initialNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY ?? node_process.default.env.NO_HISTORY;
968
- const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory$1(initialNoHistory));
1371
+ const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory(initialNoHistory));
969
1372
  const prevNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY;
970
1373
  if (shouldWriteHistory) delete node_process.default.env.CCOMMAND_NO_HISTORY;
971
1374
  else node_process.default.env.CCOMMAND_NO_HISTORY = "1";
@@ -1087,117 +1490,6 @@ async function pix(params) {
1087
1490
  }
1088
1491
  }
1089
1492
 
1090
- //#endregion
1091
- //#region src/prun.ts
1092
- async function prun(params) {
1093
- ensurePrunAutoInit();
1094
- const hadNoHistoryEnv = node_process.default.env.CCOMMAND_NO_HISTORY != null || node_process.default.env.NO_HISTORY != null;
1095
- const initialNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY ?? node_process.default.env.NO_HISTORY;
1096
- const prevNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY;
1097
- if (!(hadNoHistoryEnv && isNoHistory(initialNoHistory))) delete node_process.default.env.CCOMMAND_NO_HISTORY;
1098
- else node_process.default.env.CCOMMAND_NO_HISTORY = "1";
1099
- const { ccommand } = getCcommand();
1100
- try {
1101
- await ccommand(params);
1102
- } finally {
1103
- if (prevNoHistory == null) delete node_process.default.env.CCOMMAND_NO_HISTORY;
1104
- else node_process.default.env.CCOMMAND_NO_HISTORY = prevNoHistory;
1105
- }
1106
- }
1107
- const isZh$2 = node_process.default.env.PI_Lang === "zh";
1108
- const safeShellValue = /^[\w./:@%+=,-]+$/;
1109
- function isNoHistory(value) {
1110
- if (!value) return false;
1111
- const normalized = value.toLowerCase();
1112
- return normalized === "1" || normalized === "true" || normalized === "yes";
1113
- }
1114
- function shellQuote(value) {
1115
- if (value === "") return "''";
1116
- if (safeShellValue.test(value)) return value;
1117
- return `'${value.replace(/'/g, `'\\''`)}'`;
1118
- }
1119
- function detectShell() {
1120
- const envShell = node_process.default.env.SHELL || "";
1121
- if (node_process.default.env.FISH_VERSION) return "fish";
1122
- if (node_process.default.env.ZSH_VERSION) return "zsh";
1123
- if (node_process.default.env.BASH_VERSION) return "bash";
1124
- return envShell.split("/").pop() || "zsh";
1125
- }
1126
- function ensurePrunAutoInit() {
1127
- if (!shouldAutoInit()) return;
1128
- const shell = detectShell();
1129
- const home = node_process.default.env.HOME || node_os.default.homedir();
1130
- let rcFile = "";
1131
- let initLine = "";
1132
- if (shell === "zsh") {
1133
- const zdotdir = node_process.default.env.ZDOTDIR || home;
1134
- rcFile = node_path.default.join(zdotdir, ".zshrc");
1135
- initLine = "eval \"$(prun --init zsh)\"";
1136
- } else if (shell === "bash") {
1137
- rcFile = node_path.default.join(home, ".bashrc");
1138
- initLine = "eval \"$(prun --init bash)\"";
1139
- } else if (shell === "fish") {
1140
- const configHome = node_process.default.env.XDG_CONFIG_HOME || node_path.default.join(home, ".config");
1141
- rcFile = node_path.default.join(configHome, "fish", "config.fish");
1142
- initLine = "prun --init fish | source";
1143
- } else return;
1144
- try {
1145
- const dir = node_path.default.dirname(rcFile);
1146
- if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
1147
- const content = node_fs.default.existsSync(rcFile) ? node_fs.default.readFileSync(rcFile, "utf8") : "";
1148
- if (!/prun\s+--init/.test(content)) {
1149
- const prefix = content.length && !content.endsWith("\n") ? "\n" : "";
1150
- node_fs.default.appendFileSync(rcFile, `${prefix}${initLine}\n`, "utf8");
1151
- }
1152
- } catch {}
1153
- }
1154
- function shouldAutoInit() {
1155
- const auto = node_process.default.env.PI_AUTO_INIT || node_process.default.env.PRUN_AUTO_INIT;
1156
- if (auto != null) return isNoHistory(auto);
1157
- if (isNoHistory(node_process.default.env.PI_NO_AUTO_INIT || node_process.default.env.PRUN_NO_AUTO_INIT)) return false;
1158
- if (node_process.default.env.CI) return false;
1159
- if (!node_process.default.stdout.isTTY || !node_process.default.stdin.isTTY) return false;
1160
- return true;
1161
- }
1162
- function printPrunInit(args = []) {
1163
- const shellArg = args[0];
1164
- const binArg = args[1];
1165
- const bin = shellQuote(binArg || node_process.default.env.PRUN_BIN || "prun");
1166
- const shell = shellArg || detectShell() || "zsh";
1167
- let script = "";
1168
- if (shell === "zsh") script = [
1169
- "prun() {",
1170
- ` local bin=${bin}`,
1171
- " local -a cmd",
1172
- " cmd=(${=bin})",
1173
- " command \"${cmd[@]}\" \"$@\"",
1174
- " fc -R",
1175
- "}"
1176
- ].join("\n");
1177
- else if (shell === "bash") script = [
1178
- "prun() {",
1179
- ` local bin=${bin}`,
1180
- " local -a cmd",
1181
- " read -r -a cmd <<< \"$bin\"",
1182
- " command \"${cmd[@]}\" \"$@\"",
1183
- " history -n",
1184
- "}"
1185
- ].join("\n");
1186
- else if (shell === "fish") script = [
1187
- "function prun",
1188
- ` set -l bin ${bin}`,
1189
- " set -l cmd (string split -- \" \" $bin)",
1190
- " command $cmd $argv",
1191
- " history --merge",
1192
- "end"
1193
- ].join("\n");
1194
- else {
1195
- console.log(picocolors.default.red(isZh$2 ? `不支持的 shell: ${shell}` : `Unsupported shell: ${shell}`));
1196
- return;
1197
- }
1198
- console.log(script);
1199
- }
1200
-
1201
1493
  //#endregion
1202
1494
  //#region src/pu.ts
1203
1495
  function pu() {
package/dist/index.mjs CHANGED
@@ -12,7 +12,7 @@ import os from "node:os";
12
12
  import { fileURLToPath } from "node:url";
13
13
 
14
14
  //#region package.json
15
- var version = "0.2.9";
15
+ var version = "0.2.10";
16
16
 
17
17
  //#endregion
18
18
  //#region src/tty.ts
@@ -921,16 +921,419 @@ function getCcommand() {
921
921
  }
922
922
 
923
923
  //#endregion
924
- //#region src/pfind.ts
924
+ //#region src/prun.ts
925
+ async function prun(params) {
926
+ ensurePrunAutoInit();
927
+ const hadNoHistoryEnv = process.env.CCOMMAND_NO_HISTORY != null || process.env.NO_HISTORY != null;
928
+ const initialNoHistory = process.env.CCOMMAND_NO_HISTORY ?? process.env.NO_HISTORY;
929
+ const prevNoHistory = process.env.CCOMMAND_NO_HISTORY;
930
+ if (!(hadNoHistoryEnv && isNoHistory$1(initialNoHistory))) delete process.env.CCOMMAND_NO_HISTORY;
931
+ else process.env.CCOMMAND_NO_HISTORY = "1";
932
+ const { ccommand } = getCcommand();
933
+ try {
934
+ await ccommand(params);
935
+ } finally {
936
+ if (prevNoHistory == null) delete process.env.CCOMMAND_NO_HISTORY;
937
+ else process.env.CCOMMAND_NO_HISTORY = prevNoHistory;
938
+ }
939
+ }
940
+ const isZh$2 = process.env.PI_Lang === "zh";
941
+ const safeShellValue = /^[\w./:@%+=,-]+$/;
925
942
  function isNoHistory$1(value) {
926
943
  if (!value) return false;
927
944
  const normalized = value.toLowerCase();
928
945
  return normalized === "1" || normalized === "true" || normalized === "yes";
929
946
  }
947
+ function shellQuote(value) {
948
+ if (value === "") return "''";
949
+ if (safeShellValue.test(value)) return value;
950
+ return `'${value.replace(/'/g, `'\\''`)}'`;
951
+ }
952
+ function powerShellQuote(value) {
953
+ if (value === "") return "''";
954
+ return `'${value.replace(/'/g, "''")}'`;
955
+ }
956
+ function splitCommand(value) {
957
+ const parts = [];
958
+ let current = "";
959
+ let quote = null;
960
+ let hasValue = false;
961
+ const pushCurrent = () => {
962
+ if (!hasValue) return;
963
+ parts.push(current);
964
+ current = "";
965
+ hasValue = false;
966
+ };
967
+ for (let i = 0; i < value.length; i++) {
968
+ const char = value[i];
969
+ if (quote) {
970
+ if (char === quote) {
971
+ quote = null;
972
+ hasValue = true;
973
+ continue;
974
+ }
975
+ if (quote === "\"" && char === "\\") {
976
+ const next = value[i + 1];
977
+ if (next) {
978
+ current += next;
979
+ hasValue = true;
980
+ i++;
981
+ continue;
982
+ }
983
+ }
984
+ current += char;
985
+ hasValue = true;
986
+ continue;
987
+ }
988
+ if (char === "\"" || char === "'") {
989
+ quote = char;
990
+ hasValue = true;
991
+ continue;
992
+ }
993
+ if (/\s/.test(char)) {
994
+ pushCurrent();
995
+ while (i + 1 < value.length && /\s/.test(value[i + 1])) i++;
996
+ continue;
997
+ }
998
+ if (char === "\\") {
999
+ const next = value[i + 1];
1000
+ if (next) {
1001
+ current += next;
1002
+ hasValue = true;
1003
+ i++;
1004
+ continue;
1005
+ }
1006
+ }
1007
+ current += char;
1008
+ hasValue = true;
1009
+ }
1010
+ pushCurrent();
1011
+ return parts;
1012
+ }
1013
+ function normalizeShellName(value) {
1014
+ const shell = (value || "").toLowerCase().replace(/\.exe$/, "");
1015
+ if (shell === "powershell") return "powershell";
1016
+ if (shell === "pwsh") return "pwsh";
1017
+ if (shell === "fish" || shell === "zsh" || shell === "bash") return shell;
1018
+ return shell;
1019
+ }
1020
+ function detectShell() {
1021
+ const envShell = normalizeShellName(path.basename(process.env.SHELL || ""));
1022
+ if (process.env.FISH_VERSION) return "fish";
1023
+ if (process.env.ZSH_VERSION) return "zsh";
1024
+ if (process.env.BASH_VERSION) return "bash";
1025
+ if (process.env.POWERSHELL_DISTRIBUTION_CHANNEL) return "pwsh";
1026
+ if (envShell) return envShell;
1027
+ if (process.platform === "win32") return "powershell";
1028
+ return "zsh";
1029
+ }
1030
+ function getPowerShellProfilePath(shell, home) {
1031
+ if (process.platform === "win32") {
1032
+ const documentsHome = process.env.USERPROFILE || home;
1033
+ const profileDir = shell === "pwsh" ? "PowerShell" : "WindowsPowerShell";
1034
+ return path.join(documentsHome, "Documents", profileDir, "Microsoft.PowerShell_profile.ps1");
1035
+ }
1036
+ const configHome = process.env.XDG_CONFIG_HOME || path.join(home, ".config");
1037
+ return path.join(configHome, "powershell", "Microsoft.PowerShell_profile.ps1");
1038
+ }
1039
+ function ensurePrunAutoInit() {
1040
+ if (!shouldAutoInit()) return;
1041
+ const shell = detectShell();
1042
+ const home = process.env.HOME || os.homedir();
1043
+ let rcFile = "";
1044
+ let initLine = "";
1045
+ if (shell === "zsh") {
1046
+ const zdotdir = process.env.ZDOTDIR || home;
1047
+ rcFile = path.join(zdotdir, ".zshrc");
1048
+ initLine = "eval \"$(prun --init zsh)\"";
1049
+ } else if (shell === "bash") {
1050
+ rcFile = path.join(home, ".bashrc");
1051
+ initLine = "eval \"$(prun --init bash)\"";
1052
+ } else if (shell === "fish") {
1053
+ const configHome = process.env.XDG_CONFIG_HOME || path.join(home, ".config");
1054
+ rcFile = path.join(configHome, "fish", "config.fish");
1055
+ initLine = "prun --init fish | source";
1056
+ } else if (shell === "powershell" || shell === "pwsh") {
1057
+ rcFile = getPowerShellProfilePath(shell, home);
1058
+ initLine = `prun --init ${shell} | Out-String | Invoke-Expression`;
1059
+ } else return;
1060
+ try {
1061
+ const dir = path.dirname(rcFile);
1062
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1063
+ const content = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, "utf8") : "";
1064
+ if (!/prun\s+--init/.test(content)) {
1065
+ const prefix = content.length && !content.endsWith("\n") ? "\n" : "";
1066
+ fs.appendFileSync(rcFile, `${prefix}${initLine}\n`, "utf8");
1067
+ }
1068
+ } catch {}
1069
+ }
1070
+ function shouldAutoInit() {
1071
+ const auto = process.env.PI_AUTO_INIT || process.env.PRUN_AUTO_INIT;
1072
+ if (auto != null) return isNoHistory$1(auto);
1073
+ if (isNoHistory$1(process.env.PI_NO_AUTO_INIT || process.env.PRUN_NO_AUTO_INIT)) return false;
1074
+ if (process.env.CI) return false;
1075
+ if (!process.stdout.isTTY || !process.stdin.isTTY) return false;
1076
+ return true;
1077
+ }
1078
+ function printPrunInit(args = []) {
1079
+ const shellArg = normalizeShellName(args[0]);
1080
+ const binArg = args[1] || process.env.PRUN_BIN || "prun";
1081
+ const bin = shellQuote(binArg);
1082
+ const shell = shellArg || detectShell() || "zsh";
1083
+ const historyHintExpr = "${CCOMMAND_HISTORY_HINT:-${XDG_CACHE_HOME:-$HOME/.cache}/ccommand/last-history}";
1084
+ let script = "";
1085
+ if (shell === "zsh") script = [
1086
+ "prun() {",
1087
+ ` local bin=${bin}`,
1088
+ " local -a cmd",
1089
+ " cmd=(${=bin})",
1090
+ " command \"${cmd[@]}\" \"$@\"",
1091
+ "}",
1092
+ "__prun_sync_history() {",
1093
+ " local history_disable=${CCOMMAND_NO_HISTORY:-${NO_HISTORY:-\"\"}}",
1094
+ " local history_disable_lower=${history_disable:l}",
1095
+ " if [[ $history_disable_lower == \"1\" || $history_disable_lower == \"true\" || $history_disable_lower == \"yes\" ]]; then",
1096
+ " return",
1097
+ " fi",
1098
+ ` local history_hint=${historyHintExpr}`,
1099
+ " if [[ ! -f $history_hint ]]; then",
1100
+ " return",
1101
+ " fi",
1102
+ " local line",
1103
+ " line=$(<\"$history_hint\")",
1104
+ " local hint_ts=${line%%$'\\t'*}",
1105
+ " local hint_cmd=${line#*$'\\t'}",
1106
+ " if [[ -z $hint_ts || $hint_ts == $line ]]; then",
1107
+ " hint_cmd=$line",
1108
+ " hint_ts=\"\"",
1109
+ " fi",
1110
+ " if [[ -n $hint_ts && $hint_ts == ${__PRUN_HISTORY_HINT_TS:-\"\"} ]]; then",
1111
+ " return",
1112
+ " fi",
1113
+ " __PRUN_HISTORY_HINT_TS=$hint_ts",
1114
+ " fc -R",
1115
+ " if [[ $hint_cmd != pfind* && $hint_cmd != prun* ]]; then",
1116
+ " return",
1117
+ " fi",
1118
+ " local last_line",
1119
+ " last_line=$(fc -l -1 2>/dev/null)",
1120
+ " local last_cmd",
1121
+ " last_cmd=$(printf \"%s\" \"$last_line\" | sed -E \"s/^[[:space:]]*[0-9]+[[:space:]]*//\")",
1122
+ " if [[ $last_cmd == \"$hint_cmd\" ]]; then",
1123
+ " return",
1124
+ " fi",
1125
+ " if [[ $last_cmd == prun || $last_cmd == prun\\ * ]]; then",
1126
+ " local last_num",
1127
+ " last_num=$(printf \"%s\" \"$last_line\" | sed -E \"s/^[[:space:]]*([0-9]+).*/\\1/\")",
1128
+ " if [[ -n $last_num ]]; then",
1129
+ " history -d $last_num 2>/dev/null",
1130
+ " fi",
1131
+ " fi",
1132
+ " print -s -- \"$hint_cmd\"",
1133
+ "}",
1134
+ "",
1135
+ "if ! typeset -f __prun_precmd >/dev/null; then",
1136
+ " __prun_precmd() { __prun_sync_history }",
1137
+ " autoload -Uz add-zsh-hook",
1138
+ " add-zsh-hook precmd __prun_precmd",
1139
+ "fi"
1140
+ ].join("\n");
1141
+ else if (shell === "bash") script = [
1142
+ "prun() {",
1143
+ ` local bin=${bin}`,
1144
+ " local -a cmd",
1145
+ " read -r -a cmd <<< \"$bin\"",
1146
+ " command \"${cmd[@]}\" \"$@\"",
1147
+ "}",
1148
+ "__prun_sync_history() {",
1149
+ " local history_disable=${CCOMMAND_NO_HISTORY:-${NO_HISTORY:-\"\"}}",
1150
+ " local history_disable_lower",
1151
+ " history_disable_lower=$(printf '%s' \"$history_disable\" | tr '[:upper:]' '[:lower:]')",
1152
+ " if [[ $history_disable_lower == \"1\" || $history_disable_lower == \"true\" || $history_disable_lower == \"yes\" ]]; then",
1153
+ " return",
1154
+ " fi",
1155
+ ` local history_hint=${historyHintExpr}`,
1156
+ " if [[ ! -f $history_hint ]]; then",
1157
+ " return",
1158
+ " fi",
1159
+ " local line",
1160
+ " line=$(<\"$history_hint\")",
1161
+ " local hint_ts=\"${line%%$'\\t'*}\"",
1162
+ " local hint_cmd=\"${line#*$'\\t'}\"",
1163
+ " if [[ -z $hint_ts || $hint_ts == \"$line\" ]]; then",
1164
+ " hint_cmd=\"$line\"",
1165
+ " hint_ts=\"\"",
1166
+ " fi",
1167
+ " if [[ -n $hint_ts && $hint_ts == \"${__PRUN_HISTORY_HINT_TS:-}\" ]]; then",
1168
+ " return",
1169
+ " fi",
1170
+ " __PRUN_HISTORY_HINT_TS=$hint_ts",
1171
+ " if [[ $hint_cmd != pfind* && $hint_cmd != prun* ]]; then",
1172
+ " return",
1173
+ " fi",
1174
+ " history -n",
1175
+ " local last_line",
1176
+ " last_line=$(history 1)",
1177
+ " local last_cmd",
1178
+ " last_cmd=$(printf \"%s\" \"$last_line\" | sed -E \"s/^[[:space:]]*[0-9]+[[:space:]]*//\")",
1179
+ " if [[ $last_cmd == \"$hint_cmd\" ]]; then",
1180
+ " return",
1181
+ " fi",
1182
+ " if [[ $last_cmd == prun || $last_cmd == prun\\ * ]]; then",
1183
+ " local last_num",
1184
+ " last_num=$(printf \"%s\" \"$last_line\" | sed -E \"s/^[[:space:]]*([0-9]+).*/\\1/\")",
1185
+ " if [[ -n $last_num ]]; then",
1186
+ " history -d \"$last_num\" 2>/dev/null",
1187
+ " fi",
1188
+ " fi",
1189
+ " history -s -- \"$hint_cmd\"",
1190
+ "}",
1191
+ "",
1192
+ "if [[ -z \"${__PRUN_PROMPT_INSTALLED:-}\" ]]; then",
1193
+ " __PRUN_PROMPT_INSTALLED=1",
1194
+ " if [[ -n \"${PROMPT_COMMAND:-}\" ]]; then",
1195
+ " PROMPT_COMMAND=\"__prun_sync_history;${PROMPT_COMMAND}\"",
1196
+ " else",
1197
+ " PROMPT_COMMAND=\"__prun_sync_history\"",
1198
+ " fi",
1199
+ "fi"
1200
+ ].join("\n");
1201
+ else if (shell === "fish") script = [
1202
+ "function prun",
1203
+ ` set -l bin ${bin}`,
1204
+ " set -l cmd (string split -- \" \" $bin)",
1205
+ " command $cmd $argv",
1206
+ " set -l history_disable $CCOMMAND_NO_HISTORY",
1207
+ " if test -z \"$history_disable\"",
1208
+ " set history_disable $NO_HISTORY",
1209
+ " end",
1210
+ " set history_disable (string lower -- (string trim -- \"$history_disable\"))",
1211
+ " if test \"$history_disable\" != \"1\" -a \"$history_disable\" != \"true\" -a \"$history_disable\" != \"yes\"",
1212
+ " history --merge",
1213
+ " set -l history_hint $CCOMMAND_HISTORY_HINT",
1214
+ " if test -z \"$history_hint\"",
1215
+ " set -l cache_home $XDG_CACHE_HOME",
1216
+ " if test -z \"$cache_home\"",
1217
+ " set cache_home \"$HOME/.cache\"",
1218
+ " end",
1219
+ " set history_hint \"$cache_home/ccommand/last-history\"",
1220
+ " end",
1221
+ " if test -f \"$history_hint\"",
1222
+ " set -l last_cmd (string trim -- (cat \"$history_hint\"))",
1223
+ " set -l last_cmd (string replace -r \"^[0-9]+\\t\" \"\" -- \"$last_cmd\")",
1224
+ " if string match -q \"pfind*\" -- \"$last_cmd\"; or string match -q \"prun*\" -- \"$last_cmd\"",
1225
+ " set -l last_hist (history --max=1)",
1226
+ " if test \"$last_hist\" != \"$last_cmd\"",
1227
+ " history add -- \"$last_cmd\"",
1228
+ " end",
1229
+ " end",
1230
+ " end",
1231
+ " end",
1232
+ "end"
1233
+ ].join("\n");
1234
+ else if (shell === "powershell" || shell === "pwsh") script = [
1235
+ `$script:__prun_bin = @(${splitCommand(binArg).map(powerShellQuote).join(", ") || powerShellQuote("prun")})`,
1236
+ "",
1237
+ "function global:prun {",
1238
+ " param([Parameter(ValueFromRemainingArguments = $true)] [string[]] $CliArgs)",
1239
+ " $command = $script:__prun_bin[0]",
1240
+ " if ($command -ieq \"prun\") {",
1241
+ " $resolved = Get-Command -Name $command -CommandType Application,ExternalScript -ErrorAction SilentlyContinue | Select-Object -First 1",
1242
+ " if ($null -ne $resolved) {",
1243
+ " if ($resolved.Path) { $command = $resolved.Path }",
1244
+ " elseif ($resolved.Definition) { $command = $resolved.Definition }",
1245
+ " elseif ($resolved.Source) { $command = $resolved.Source }",
1246
+ " }",
1247
+ " }",
1248
+ " $extra = @()",
1249
+ " if ($script:__prun_bin.Count -gt 1) {",
1250
+ " $extra = $script:__prun_bin[1..($script:__prun_bin.Count - 1)]",
1251
+ " }",
1252
+ " & $command @extra @CliArgs",
1253
+ "}",
1254
+ "",
1255
+ "function global:__prun_sync_history {",
1256
+ " $historyDisable = $env:CCOMMAND_NO_HISTORY",
1257
+ " if ([string]::IsNullOrWhiteSpace($historyDisable)) {",
1258
+ " $historyDisable = $env:NO_HISTORY",
1259
+ " }",
1260
+ " $historyDisable = \"$historyDisable\".Trim().ToLowerInvariant()",
1261
+ " if ($historyDisable -eq \"1\" -or $historyDisable -eq \"true\" -or $historyDisable -eq \"yes\") {",
1262
+ " return",
1263
+ " }",
1264
+ " $historyHint = $env:CCOMMAND_HISTORY_HINT",
1265
+ " if ([string]::IsNullOrWhiteSpace($historyHint)) {",
1266
+ " $cacheHome = $env:XDG_CACHE_HOME",
1267
+ " if ([string]::IsNullOrWhiteSpace($cacheHome)) {",
1268
+ " $cacheHome = Join-Path $HOME \".cache\"",
1269
+ " }",
1270
+ " $historyHint = Join-Path (Join-Path $cacheHome \"ccommand\") \"last-history\"",
1271
+ " }",
1272
+ " if (-not (Test-Path -LiteralPath $historyHint)) {",
1273
+ " return",
1274
+ " }",
1275
+ " $line = (Get-Content -LiteralPath $historyHint -Raw -ErrorAction SilentlyContinue)",
1276
+ " if ([string]::IsNullOrWhiteSpace($line)) {",
1277
+ " return",
1278
+ " }",
1279
+ " $line = $line.Trim()",
1280
+ " $hintTs = \"\"",
1281
+ " $hintCmd = $line",
1282
+ " $parts = $line -split \"`t\", 2",
1283
+ " if ($parts.Count -eq 2 -and $parts[0] -match \"^\\d+$\") {",
1284
+ " $hintTs = $parts[0]",
1285
+ " $hintCmd = $parts[1]",
1286
+ " }",
1287
+ " if (-not $hintCmd.StartsWith(\"prun\") -and -not $hintCmd.StartsWith(\"pfind\")) {",
1288
+ " return",
1289
+ " }",
1290
+ " if ($hintTs -and $script:__PRUN_HISTORY_HINT_TS -eq $hintTs) {",
1291
+ " return",
1292
+ " }",
1293
+ " $script:__PRUN_HISTORY_HINT_TS = $hintTs",
1294
+ " $psReadLineType = \"Microsoft.PowerShell.PSConsoleReadLine\" -as [type]",
1295
+ " if ($null -eq $psReadLineType) {",
1296
+ " return",
1297
+ " }",
1298
+ " $psReadLineType::AddToHistory($hintCmd)",
1299
+ "}",
1300
+ "",
1301
+ "if (-not $script:__PRUN_PROMPT_INSTALLED) {",
1302
+ " $script:__PRUN_PROMPT_INSTALLED = $true",
1303
+ " if (-not $script:__prun_original_prompt) {",
1304
+ " if (Test-Path Function:\\prompt) {",
1305
+ " $script:__prun_original_prompt = $function:prompt",
1306
+ " }",
1307
+ " }",
1308
+ " function global:prompt {",
1309
+ " __prun_sync_history",
1310
+ " if ($script:__prun_original_prompt) {",
1311
+ " & $script:__prun_original_prompt",
1312
+ " return",
1313
+ " }",
1314
+ " \"PS $($executionContext.SessionState.Path.CurrentLocation)> \"",
1315
+ " }",
1316
+ "}"
1317
+ ].join("\n");
1318
+ else {
1319
+ console.log(color.red(isZh$2 ? `不支持的 shell: ${shell}` : `Unsupported shell: ${shell}`));
1320
+ return;
1321
+ }
1322
+ console.log(script);
1323
+ }
1324
+
1325
+ //#endregion
1326
+ //#region src/pfind.ts
1327
+ function isNoHistory(value) {
1328
+ if (!value) return false;
1329
+ const normalized = value.toLowerCase();
1330
+ return normalized === "1" || normalized === "true" || normalized === "yes";
1331
+ }
930
1332
  async function pfind(params) {
1333
+ ensurePrunAutoInit();
931
1334
  const hadNoHistoryEnv = process.env.CCOMMAND_NO_HISTORY != null || process.env.NO_HISTORY != null;
932
1335
  const initialNoHistory = process.env.CCOMMAND_NO_HISTORY ?? process.env.NO_HISTORY;
933
- const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory$1(initialNoHistory));
1336
+ const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory(initialNoHistory));
934
1337
  const prevNoHistory = process.env.CCOMMAND_NO_HISTORY;
935
1338
  if (shouldWriteHistory) delete process.env.CCOMMAND_NO_HISTORY;
936
1339
  else process.env.CCOMMAND_NO_HISTORY = "1";
@@ -1052,117 +1455,6 @@ async function pix(params) {
1052
1455
  }
1053
1456
  }
1054
1457
 
1055
- //#endregion
1056
- //#region src/prun.ts
1057
- async function prun(params) {
1058
- ensurePrunAutoInit();
1059
- const hadNoHistoryEnv = process.env.CCOMMAND_NO_HISTORY != null || process.env.NO_HISTORY != null;
1060
- const initialNoHistory = process.env.CCOMMAND_NO_HISTORY ?? process.env.NO_HISTORY;
1061
- const prevNoHistory = process.env.CCOMMAND_NO_HISTORY;
1062
- if (!(hadNoHistoryEnv && isNoHistory(initialNoHistory))) delete process.env.CCOMMAND_NO_HISTORY;
1063
- else process.env.CCOMMAND_NO_HISTORY = "1";
1064
- const { ccommand } = getCcommand();
1065
- try {
1066
- await ccommand(params);
1067
- } finally {
1068
- if (prevNoHistory == null) delete process.env.CCOMMAND_NO_HISTORY;
1069
- else process.env.CCOMMAND_NO_HISTORY = prevNoHistory;
1070
- }
1071
- }
1072
- const isZh$2 = process.env.PI_Lang === "zh";
1073
- const safeShellValue = /^[\w./:@%+=,-]+$/;
1074
- function isNoHistory(value) {
1075
- if (!value) return false;
1076
- const normalized = value.toLowerCase();
1077
- return normalized === "1" || normalized === "true" || normalized === "yes";
1078
- }
1079
- function shellQuote(value) {
1080
- if (value === "") return "''";
1081
- if (safeShellValue.test(value)) return value;
1082
- return `'${value.replace(/'/g, `'\\''`)}'`;
1083
- }
1084
- function detectShell() {
1085
- const envShell = process.env.SHELL || "";
1086
- if (process.env.FISH_VERSION) return "fish";
1087
- if (process.env.ZSH_VERSION) return "zsh";
1088
- if (process.env.BASH_VERSION) return "bash";
1089
- return envShell.split("/").pop() || "zsh";
1090
- }
1091
- function ensurePrunAutoInit() {
1092
- if (!shouldAutoInit()) return;
1093
- const shell = detectShell();
1094
- const home = process.env.HOME || os.homedir();
1095
- let rcFile = "";
1096
- let initLine = "";
1097
- if (shell === "zsh") {
1098
- const zdotdir = process.env.ZDOTDIR || home;
1099
- rcFile = path.join(zdotdir, ".zshrc");
1100
- initLine = "eval \"$(prun --init zsh)\"";
1101
- } else if (shell === "bash") {
1102
- rcFile = path.join(home, ".bashrc");
1103
- initLine = "eval \"$(prun --init bash)\"";
1104
- } else if (shell === "fish") {
1105
- const configHome = process.env.XDG_CONFIG_HOME || path.join(home, ".config");
1106
- rcFile = path.join(configHome, "fish", "config.fish");
1107
- initLine = "prun --init fish | source";
1108
- } else return;
1109
- try {
1110
- const dir = path.dirname(rcFile);
1111
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1112
- const content = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, "utf8") : "";
1113
- if (!/prun\s+--init/.test(content)) {
1114
- const prefix = content.length && !content.endsWith("\n") ? "\n" : "";
1115
- fs.appendFileSync(rcFile, `${prefix}${initLine}\n`, "utf8");
1116
- }
1117
- } catch {}
1118
- }
1119
- function shouldAutoInit() {
1120
- const auto = process.env.PI_AUTO_INIT || process.env.PRUN_AUTO_INIT;
1121
- if (auto != null) return isNoHistory(auto);
1122
- if (isNoHistory(process.env.PI_NO_AUTO_INIT || process.env.PRUN_NO_AUTO_INIT)) return false;
1123
- if (process.env.CI) return false;
1124
- if (!process.stdout.isTTY || !process.stdin.isTTY) return false;
1125
- return true;
1126
- }
1127
- function printPrunInit(args = []) {
1128
- const shellArg = args[0];
1129
- const binArg = args[1];
1130
- const bin = shellQuote(binArg || process.env.PRUN_BIN || "prun");
1131
- const shell = shellArg || detectShell() || "zsh";
1132
- let script = "";
1133
- if (shell === "zsh") script = [
1134
- "prun() {",
1135
- ` local bin=${bin}`,
1136
- " local -a cmd",
1137
- " cmd=(${=bin})",
1138
- " command \"${cmd[@]}\" \"$@\"",
1139
- " fc -R",
1140
- "}"
1141
- ].join("\n");
1142
- else if (shell === "bash") script = [
1143
- "prun() {",
1144
- ` local bin=${bin}`,
1145
- " local -a cmd",
1146
- " read -r -a cmd <<< \"$bin\"",
1147
- " command \"${cmd[@]}\" \"$@\"",
1148
- " history -n",
1149
- "}"
1150
- ].join("\n");
1151
- else if (shell === "fish") script = [
1152
- "function prun",
1153
- ` set -l bin ${bin}`,
1154
- " set -l cmd (string split -- \" \" $bin)",
1155
- " command $cmd $argv",
1156
- " history --merge",
1157
- "end"
1158
- ].join("\n");
1159
- else {
1160
- console.log(color.red(isZh$2 ? `不支持的 shell: ${shell}` : `Unsupported shell: ${shell}`));
1161
- return;
1162
- }
1163
- console.log(script);
1164
- }
1165
-
1166
1458
  //#endregion
1167
1459
  //#region src/pu.ts
1168
1460
  function pu() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@simon_he/pi",
3
3
  "type": "module",
4
- "version": "0.2.9",
4
+ "version": "0.2.10",
5
5
  "packageManager": "pnpm@10.29.1",
6
6
  "description": "An intelligent cross-platform package manager and CLI tool that autodetects project environments (Node.mjs, Go, Rust, Python) with beautiful loading animations and smart command execution.",
7
7
  "author": {