@simon_he/pi 0.2.8 → 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.8";
50
+ var version = "0.2.10";
51
51
 
52
52
  //#endregion
53
53
  //#region src/tty.ts
@@ -557,8 +557,30 @@ async function detectNode() {
557
557
 
558
558
  //#endregion
559
559
  //#region src/pkgManager.ts
560
+ function normalizeDir$1(dir) {
561
+ return node_path.default.resolve(dir);
562
+ }
563
+ function findUpSync$1(startDir, predicate) {
564
+ let current = normalizeDir$1(startDir);
565
+ while (true) {
566
+ if (predicate(current)) return current;
567
+ const parent = node_path.default.dirname(current);
568
+ if (parent === current) return null;
569
+ current = parent;
570
+ }
571
+ }
572
+ function inferToolFromRepoLayout(cwd) {
573
+ if (findUpSync$1(cwd, (dir) => (0, lazy_js_utils.isFile)(node_path.default.join(dir, "pnpm-workspace.yaml")) || (0, lazy_js_utils.isFile)(node_path.default.join(dir, "pnpm-lock.yaml")))) return "pnpm";
574
+ if (findUpSync$1(cwd, (dir) => (0, lazy_js_utils.isFile)(node_path.default.join(dir, "yarn.lock")) || (0, lazy_js_utils.isFile)(node_path.default.join(dir, ".yarnrc.yml")))) return "yarn";
575
+ if (findUpSync$1(cwd, (dir) => (0, lazy_js_utils.isFile)(node_path.default.join(dir, "bun.lockb")))) return "bun";
576
+ return null;
577
+ }
560
578
  async function resolvePkgTool() {
561
- const detected = await (0, lazy_js_utils_node.getPkgTool)() || "npm";
579
+ let detected = await (0, lazy_js_utils_node.getPkgTool)() || "npm";
580
+ if (detected === "npm") {
581
+ const inferred = inferToolFromRepoLayout(node_process.default.cwd());
582
+ if (inferred) detected = inferred;
583
+ }
562
584
  const fallback = node_process.default.env.PI_DEFAULT;
563
585
  return {
564
586
  detected,
@@ -622,7 +644,13 @@ async function findUpAsync(startDir, predicate) {
622
644
  async function getParams(params) {
623
645
  const cwd = node_process.default.cwd();
624
646
  try {
625
- switch (await (0, lazy_js_utils_node.getPkgTool)()) {
647
+ let tool = await (0, lazy_js_utils_node.getPkgTool)() || "npm";
648
+ if (tool === "npm") {
649
+ if (findUpSync(cwd, (dir) => (0, lazy_js_utils.isFile)(node_path.default.join(dir, "pnpm-workspace.yaml")) || (0, lazy_js_utils.isFile)(node_path.default.join(dir, "pnpm-lock.yaml")))) tool = "pnpm";
650
+ else if (findUpSync(cwd, (dir) => (0, lazy_js_utils.isFile)(node_path.default.join(dir, "yarn.lock")) || (0, lazy_js_utils.isFile)(node_path.default.join(dir, ".yarnrc.yml")))) tool = "yarn";
651
+ else if (findUpSync(cwd, (dir) => (0, lazy_js_utils.isFile)(node_path.default.join(dir, "bun.lockb")))) tool = "bun";
652
+ }
653
+ switch (tool) {
626
654
  case "pnpm": {
627
655
  const pnpmWorkspaceRoot = findUpSync(cwd, (dir) => (0, lazy_js_utils.isFile)(node_path.default.join(dir, "pnpm-workspace.yaml")));
628
656
  const inPnpmWorkspace = Boolean(pnpmWorkspaceRoot);
@@ -928,16 +956,419 @@ function getCcommand() {
928
956
  }
929
957
 
930
958
  //#endregion
931
- //#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./:@%+=,-]+$/;
932
977
  function isNoHistory$1(value) {
933
978
  if (!value) return false;
934
979
  const normalized = value.toLowerCase();
935
980
  return normalized === "1" || normalized === "true" || normalized === "yes";
936
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
+ }
937
1367
  async function pfind(params) {
1368
+ ensurePrunAutoInit();
938
1369
  const hadNoHistoryEnv = node_process.default.env.CCOMMAND_NO_HISTORY != null || node_process.default.env.NO_HISTORY != null;
939
1370
  const initialNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY ?? node_process.default.env.NO_HISTORY;
940
- const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory$1(initialNoHistory));
1371
+ const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory(initialNoHistory));
941
1372
  const prevNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY;
942
1373
  if (shouldWriteHistory) delete node_process.default.env.CCOMMAND_NO_HISTORY;
943
1374
  else node_process.default.env.CCOMMAND_NO_HISTORY = "1";
@@ -1059,117 +1490,6 @@ async function pix(params) {
1059
1490
  }
1060
1491
  }
1061
1492
 
1062
- //#endregion
1063
- //#region src/prun.ts
1064
- async function prun(params) {
1065
- ensurePrunAutoInit();
1066
- const hadNoHistoryEnv = node_process.default.env.CCOMMAND_NO_HISTORY != null || node_process.default.env.NO_HISTORY != null;
1067
- const initialNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY ?? node_process.default.env.NO_HISTORY;
1068
- const prevNoHistory = node_process.default.env.CCOMMAND_NO_HISTORY;
1069
- if (!(hadNoHistoryEnv && isNoHistory(initialNoHistory))) delete node_process.default.env.CCOMMAND_NO_HISTORY;
1070
- else node_process.default.env.CCOMMAND_NO_HISTORY = "1";
1071
- const { ccommand } = getCcommand();
1072
- try {
1073
- await ccommand(params);
1074
- } finally {
1075
- if (prevNoHistory == null) delete node_process.default.env.CCOMMAND_NO_HISTORY;
1076
- else node_process.default.env.CCOMMAND_NO_HISTORY = prevNoHistory;
1077
- }
1078
- }
1079
- const isZh$2 = node_process.default.env.PI_Lang === "zh";
1080
- const safeShellValue = /^[\w./:@%+=,-]+$/;
1081
- function isNoHistory(value) {
1082
- if (!value) return false;
1083
- const normalized = value.toLowerCase();
1084
- return normalized === "1" || normalized === "true" || normalized === "yes";
1085
- }
1086
- function shellQuote(value) {
1087
- if (value === "") return "''";
1088
- if (safeShellValue.test(value)) return value;
1089
- return `'${value.replace(/'/g, `'\\''`)}'`;
1090
- }
1091
- function detectShell() {
1092
- const envShell = node_process.default.env.SHELL || "";
1093
- if (node_process.default.env.FISH_VERSION) return "fish";
1094
- if (node_process.default.env.ZSH_VERSION) return "zsh";
1095
- if (node_process.default.env.BASH_VERSION) return "bash";
1096
- return envShell.split("/").pop() || "zsh";
1097
- }
1098
- function ensurePrunAutoInit() {
1099
- if (!shouldAutoInit()) return;
1100
- const shell = detectShell();
1101
- const home = node_process.default.env.HOME || node_os.default.homedir();
1102
- let rcFile = "";
1103
- let initLine = "";
1104
- if (shell === "zsh") {
1105
- const zdotdir = node_process.default.env.ZDOTDIR || home;
1106
- rcFile = node_path.default.join(zdotdir, ".zshrc");
1107
- initLine = "eval \"$(prun --init zsh)\"";
1108
- } else if (shell === "bash") {
1109
- rcFile = node_path.default.join(home, ".bashrc");
1110
- initLine = "eval \"$(prun --init bash)\"";
1111
- } else if (shell === "fish") {
1112
- const configHome = node_process.default.env.XDG_CONFIG_HOME || node_path.default.join(home, ".config");
1113
- rcFile = node_path.default.join(configHome, "fish", "config.fish");
1114
- initLine = "prun --init fish | source";
1115
- } else return;
1116
- try {
1117
- const dir = node_path.default.dirname(rcFile);
1118
- if (!node_fs.default.existsSync(dir)) node_fs.default.mkdirSync(dir, { recursive: true });
1119
- const content = node_fs.default.existsSync(rcFile) ? node_fs.default.readFileSync(rcFile, "utf8") : "";
1120
- if (!/prun\s+--init/.test(content)) {
1121
- const prefix = content.length && !content.endsWith("\n") ? "\n" : "";
1122
- node_fs.default.appendFileSync(rcFile, `${prefix}${initLine}\n`, "utf8");
1123
- }
1124
- } catch {}
1125
- }
1126
- function shouldAutoInit() {
1127
- const auto = node_process.default.env.PI_AUTO_INIT || node_process.default.env.PRUN_AUTO_INIT;
1128
- if (auto != null) return isNoHistory(auto);
1129
- if (isNoHistory(node_process.default.env.PI_NO_AUTO_INIT || node_process.default.env.PRUN_NO_AUTO_INIT)) return false;
1130
- if (node_process.default.env.CI) return false;
1131
- if (!node_process.default.stdout.isTTY || !node_process.default.stdin.isTTY) return false;
1132
- return true;
1133
- }
1134
- function printPrunInit(args = []) {
1135
- const shellArg = args[0];
1136
- const binArg = args[1];
1137
- const bin = shellQuote(binArg || node_process.default.env.PRUN_BIN || "prun");
1138
- const shell = shellArg || detectShell() || "zsh";
1139
- let script = "";
1140
- if (shell === "zsh") script = [
1141
- "prun() {",
1142
- ` local bin=${bin}`,
1143
- " local -a cmd",
1144
- " cmd=(${=bin})",
1145
- " command \"${cmd[@]}\" \"$@\"",
1146
- " fc -R",
1147
- "}"
1148
- ].join("\n");
1149
- else if (shell === "bash") script = [
1150
- "prun() {",
1151
- ` local bin=${bin}`,
1152
- " local -a cmd",
1153
- " read -r -a cmd <<< \"$bin\"",
1154
- " command \"${cmd[@]}\" \"$@\"",
1155
- " history -n",
1156
- "}"
1157
- ].join("\n");
1158
- else if (shell === "fish") script = [
1159
- "function prun",
1160
- ` set -l bin ${bin}`,
1161
- " set -l cmd (string split -- \" \" $bin)",
1162
- " command $cmd $argv",
1163
- " history --merge",
1164
- "end"
1165
- ].join("\n");
1166
- else {
1167
- console.log(picocolors.default.red(isZh$2 ? `不支持的 shell: ${shell}` : `Unsupported shell: ${shell}`));
1168
- return;
1169
- }
1170
- console.log(script);
1171
- }
1172
-
1173
1493
  //#endregion
1174
1494
  //#region src/pu.ts
1175
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.8";
15
+ var version = "0.2.10";
16
16
 
17
17
  //#endregion
18
18
  //#region src/tty.ts
@@ -522,8 +522,30 @@ async function detectNode() {
522
522
 
523
523
  //#endregion
524
524
  //#region src/pkgManager.ts
525
+ function normalizeDir$1(dir) {
526
+ return path.resolve(dir);
527
+ }
528
+ function findUpSync$1(startDir, predicate) {
529
+ let current = normalizeDir$1(startDir);
530
+ while (true) {
531
+ if (predicate(current)) return current;
532
+ const parent = path.dirname(current);
533
+ if (parent === current) return null;
534
+ current = parent;
535
+ }
536
+ }
537
+ function inferToolFromRepoLayout(cwd) {
538
+ if (findUpSync$1(cwd, (dir) => isFile(path.join(dir, "pnpm-workspace.yaml")) || isFile(path.join(dir, "pnpm-lock.yaml")))) return "pnpm";
539
+ if (findUpSync$1(cwd, (dir) => isFile(path.join(dir, "yarn.lock")) || isFile(path.join(dir, ".yarnrc.yml")))) return "yarn";
540
+ if (findUpSync$1(cwd, (dir) => isFile(path.join(dir, "bun.lockb")))) return "bun";
541
+ return null;
542
+ }
525
543
  async function resolvePkgTool() {
526
- const detected = await getPkgTool() || "npm";
544
+ let detected = await getPkgTool() || "npm";
545
+ if (detected === "npm") {
546
+ const inferred = inferToolFromRepoLayout(process.cwd());
547
+ if (inferred) detected = inferred;
548
+ }
527
549
  const fallback = process.env.PI_DEFAULT;
528
550
  return {
529
551
  detected,
@@ -587,7 +609,13 @@ async function findUpAsync(startDir, predicate) {
587
609
  async function getParams(params) {
588
610
  const cwd = process.cwd();
589
611
  try {
590
- switch (await getPkgTool()) {
612
+ let tool = await getPkgTool() || "npm";
613
+ if (tool === "npm") {
614
+ if (findUpSync(cwd, (dir) => isFile(path.join(dir, "pnpm-workspace.yaml")) || isFile(path.join(dir, "pnpm-lock.yaml")))) tool = "pnpm";
615
+ else if (findUpSync(cwd, (dir) => isFile(path.join(dir, "yarn.lock")) || isFile(path.join(dir, ".yarnrc.yml")))) tool = "yarn";
616
+ else if (findUpSync(cwd, (dir) => isFile(path.join(dir, "bun.lockb")))) tool = "bun";
617
+ }
618
+ switch (tool) {
591
619
  case "pnpm": {
592
620
  const pnpmWorkspaceRoot = findUpSync(cwd, (dir) => isFile(path.join(dir, "pnpm-workspace.yaml")));
593
621
  const inPnpmWorkspace = Boolean(pnpmWorkspaceRoot);
@@ -893,16 +921,419 @@ function getCcommand() {
893
921
  }
894
922
 
895
923
  //#endregion
896
- //#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./:@%+=,-]+$/;
897
942
  function isNoHistory$1(value) {
898
943
  if (!value) return false;
899
944
  const normalized = value.toLowerCase();
900
945
  return normalized === "1" || normalized === "true" || normalized === "yes";
901
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
+ }
902
1332
  async function pfind(params) {
1333
+ ensurePrunAutoInit();
903
1334
  const hadNoHistoryEnv = process.env.CCOMMAND_NO_HISTORY != null || process.env.NO_HISTORY != null;
904
1335
  const initialNoHistory = process.env.CCOMMAND_NO_HISTORY ?? process.env.NO_HISTORY;
905
- const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory$1(initialNoHistory));
1336
+ const shouldWriteHistory = !(hadNoHistoryEnv && isNoHistory(initialNoHistory));
906
1337
  const prevNoHistory = process.env.CCOMMAND_NO_HISTORY;
907
1338
  if (shouldWriteHistory) delete process.env.CCOMMAND_NO_HISTORY;
908
1339
  else process.env.CCOMMAND_NO_HISTORY = "1";
@@ -1024,117 +1455,6 @@ async function pix(params) {
1024
1455
  }
1025
1456
  }
1026
1457
 
1027
- //#endregion
1028
- //#region src/prun.ts
1029
- async function prun(params) {
1030
- ensurePrunAutoInit();
1031
- const hadNoHistoryEnv = process.env.CCOMMAND_NO_HISTORY != null || process.env.NO_HISTORY != null;
1032
- const initialNoHistory = process.env.CCOMMAND_NO_HISTORY ?? process.env.NO_HISTORY;
1033
- const prevNoHistory = process.env.CCOMMAND_NO_HISTORY;
1034
- if (!(hadNoHistoryEnv && isNoHistory(initialNoHistory))) delete process.env.CCOMMAND_NO_HISTORY;
1035
- else process.env.CCOMMAND_NO_HISTORY = "1";
1036
- const { ccommand } = getCcommand();
1037
- try {
1038
- await ccommand(params);
1039
- } finally {
1040
- if (prevNoHistory == null) delete process.env.CCOMMAND_NO_HISTORY;
1041
- else process.env.CCOMMAND_NO_HISTORY = prevNoHistory;
1042
- }
1043
- }
1044
- const isZh$2 = process.env.PI_Lang === "zh";
1045
- const safeShellValue = /^[\w./:@%+=,-]+$/;
1046
- function isNoHistory(value) {
1047
- if (!value) return false;
1048
- const normalized = value.toLowerCase();
1049
- return normalized === "1" || normalized === "true" || normalized === "yes";
1050
- }
1051
- function shellQuote(value) {
1052
- if (value === "") return "''";
1053
- if (safeShellValue.test(value)) return value;
1054
- return `'${value.replace(/'/g, `'\\''`)}'`;
1055
- }
1056
- function detectShell() {
1057
- const envShell = process.env.SHELL || "";
1058
- if (process.env.FISH_VERSION) return "fish";
1059
- if (process.env.ZSH_VERSION) return "zsh";
1060
- if (process.env.BASH_VERSION) return "bash";
1061
- return envShell.split("/").pop() || "zsh";
1062
- }
1063
- function ensurePrunAutoInit() {
1064
- if (!shouldAutoInit()) return;
1065
- const shell = detectShell();
1066
- const home = process.env.HOME || os.homedir();
1067
- let rcFile = "";
1068
- let initLine = "";
1069
- if (shell === "zsh") {
1070
- const zdotdir = process.env.ZDOTDIR || home;
1071
- rcFile = path.join(zdotdir, ".zshrc");
1072
- initLine = "eval \"$(prun --init zsh)\"";
1073
- } else if (shell === "bash") {
1074
- rcFile = path.join(home, ".bashrc");
1075
- initLine = "eval \"$(prun --init bash)\"";
1076
- } else if (shell === "fish") {
1077
- const configHome = process.env.XDG_CONFIG_HOME || path.join(home, ".config");
1078
- rcFile = path.join(configHome, "fish", "config.fish");
1079
- initLine = "prun --init fish | source";
1080
- } else return;
1081
- try {
1082
- const dir = path.dirname(rcFile);
1083
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1084
- const content = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, "utf8") : "";
1085
- if (!/prun\s+--init/.test(content)) {
1086
- const prefix = content.length && !content.endsWith("\n") ? "\n" : "";
1087
- fs.appendFileSync(rcFile, `${prefix}${initLine}\n`, "utf8");
1088
- }
1089
- } catch {}
1090
- }
1091
- function shouldAutoInit() {
1092
- const auto = process.env.PI_AUTO_INIT || process.env.PRUN_AUTO_INIT;
1093
- if (auto != null) return isNoHistory(auto);
1094
- if (isNoHistory(process.env.PI_NO_AUTO_INIT || process.env.PRUN_NO_AUTO_INIT)) return false;
1095
- if (process.env.CI) return false;
1096
- if (!process.stdout.isTTY || !process.stdin.isTTY) return false;
1097
- return true;
1098
- }
1099
- function printPrunInit(args = []) {
1100
- const shellArg = args[0];
1101
- const binArg = args[1];
1102
- const bin = shellQuote(binArg || process.env.PRUN_BIN || "prun");
1103
- const shell = shellArg || detectShell() || "zsh";
1104
- let script = "";
1105
- if (shell === "zsh") script = [
1106
- "prun() {",
1107
- ` local bin=${bin}`,
1108
- " local -a cmd",
1109
- " cmd=(${=bin})",
1110
- " command \"${cmd[@]}\" \"$@\"",
1111
- " fc -R",
1112
- "}"
1113
- ].join("\n");
1114
- else if (shell === "bash") script = [
1115
- "prun() {",
1116
- ` local bin=${bin}`,
1117
- " local -a cmd",
1118
- " read -r -a cmd <<< \"$bin\"",
1119
- " command \"${cmd[@]}\" \"$@\"",
1120
- " history -n",
1121
- "}"
1122
- ].join("\n");
1123
- else if (shell === "fish") script = [
1124
- "function prun",
1125
- ` set -l bin ${bin}`,
1126
- " set -l cmd (string split -- \" \" $bin)",
1127
- " command $cmd $argv",
1128
- " history --merge",
1129
- "end"
1130
- ].join("\n");
1131
- else {
1132
- console.log(color.red(isZh$2 ? `不支持的 shell: ${shell}` : `Unsupported shell: ${shell}`));
1133
- return;
1134
- }
1135
- console.log(script);
1136
- }
1137
-
1138
1458
  //#endregion
1139
1459
  //#region src/pu.ts
1140
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.8",
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": {