Files
my-pi/install.sh
T
云服务部-叶林立andDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 2682bd7fa7 feat: 组合包配置集中到 my-pi/ 目录并改为整体重建
- 组合包拥有、可随时重建的配置、日志与缓存统一放在
  <agentDir>/my-pi/:permission-system/(policy.jsonc、hard-guards.jsonc、
  logs/)、rtk-optimizer/config.json、tool-search/(config.json、
  catalog-v1.json)与 system-prompt-cover.md。hashline.json、lsp.json
  由外部扩展读取固定路径,是唯一例外。
- 新增 config/bundle-manifest.json 与 scripts/rebuild-bundle-config.mjs:
  先校验全部来源与目标,在临时目录构建完整树,再整体删除旧 my-pi/
  (符号链接只删链接)并原子替换,不备份。install.sh / update.sh 每次
  运行都调用它;扩展自身不再播种或迁移配置。
- update.sh 删除 pi update 失败后的 git clean 恢复逻辑:Pi 1.3.0 会在
  下一次 pi update 时依据中断标记自行恢复。同时删除 Tool Search
  settings.json 快照迁移与封面部署脚本。
- Tool Search 改为读取 my-pi/tool-search/config.json(源为
  config/tool-search.json,须与 BUNDLE_TOOL_SEARCH_DEFAULTS 一致),
  不再读写 settings.json 的 toolSearch 段,移除 bundleDefaultsVersion。
- 默认权限策略开启 debugLog。
- 旧位置的残留文件不迁移、不清理;AGENTS.md / README 同步新约束。

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-19 02:26:57 -07:00

486 lines
14 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
set -u
SCRIPT_DIR=$(CDPATH=; cd "$(dirname "$0")" && pwd) || exit 1
MANAGED_CONFIG_HELPERS=$SCRIPT_DIR/scripts/managed-config.sh
if [ ! -f "$MANAGED_CONFIG_HELPERS" ]; then
printf '错误: 找不到受管配置辅助脚本:%s\n' "$MANAGED_CONFIG_HELPERS" >&2
exit 1
fi
# shellcheck source=scripts/managed-config.sh
. "$MANAGED_CONFIG_HELPERS"
PACKAGE_SOURCE=${PI_PACKAGE_SOURCE:-git:git@bitbucket.org:siakitem/my-pi.git}
POWERLEVEL10K_PRESET_FILE=$SCRIPT_DIR/config/p10k.zsh
CODEGRAPH_INSTALL_URL=https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh
KITTY_INSTALL_URL=https://sw.kovidgoyal.net/kitty/installer.sh
OH_MY_ZSH_INSTALL_URL=https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
POWERLEVEL10K_REPOSITORY=https://github.com/romkatv/powerlevel10k.git
ZSH_AUTOSUGGESTIONS_REPOSITORY=https://github.com/zsh-users/zsh-autosuggestions.git
ZSH_SYNTAX_HIGHLIGHTING_REPOSITORY=https://github.com/zsh-users/zsh-syntax-highlighting.git
install_failed=0
say() {
printf '%s\n' "$*"
}
warn() {
printf '警告: %s\n' "$*" >&2
}
ask_yes_no() {
prompt=$1
printf '%s [y/N] ' "$prompt"
if ! IFS= read -r answer; then
answer=n
fi
case $answer in
y|Y|yes|YES|Yes) return 0 ;;
*) return 1 ;;
esac
}
ask_to_install() {
ask_yes_no "未检测到 $1。是否现在安装?"
}
ask_to_enable() {
ask_yes_no "尚未启用 $1。是否现在安装所需文件并启用?"
}
require_brew() {
if command -v brew >/dev/null 2>&1; then
return 0
fi
warn "自动安装该依赖需要 Homebrew,但当前 PATH 中没有 brew。"
warn "请先安装 Homebrew(https://brew.sh/),然后重新运行本脚本。"
return 1
}
record_install_failure() {
warn "$1 安装失败;其余依赖仍会继续检查。"
install_failed=1
}
resolve_agent_dir() {
if [ -n "${PI_CODING_AGENT_DIR:-}" ]; then
case $PI_CODING_AGENT_DIR in
~/*) printf '%s\n' "$HOME/${PI_CODING_AGENT_DIR#~/}" ;;
*) printf '%s\n' "$PI_CODING_AGENT_DIR" ;;
esac
return 0
fi
printf '%s\n' "$HOME/.pi/agent"
}
# 整体删除并重建组合包拥有的 <agentDir>/my-pi/ 配置目录;脚本目录内的组合包副本即为来源。
rebuild_bundle_config() {
rebuild_helper=$SCRIPT_DIR/scripts/rebuild-bundle-config.mjs
if [ ! -f "$rebuild_helper" ]; then
warn "找不到组合包配置重建辅助脚本,已跳过配置重建。"
return 1
fi
if ! command -v node >/dev/null 2>&1; then
warn "找不到 node,无法重建组合包配置目录。"
return 1
fi
rebuild_agent_dir=$(resolve_agent_dir) || return 1
rebuild_status=$(node "$rebuild_helper" "$rebuild_agent_dir" 2>&1) || {
warn "组合包配置目录重建失败:$rebuild_status"
return 1
}
case $rebuild_status in
rebuilt:*) say "✓ 已重建组合包配置目录:${rebuild_status#rebuilt:}" ;;
*)
warn "组合包配置重建返回未知状态:$rebuild_status"
return 1
;;
esac
}
java_major_version() {
java_version=$(java -version 2>&1 | awk -F '"' 'NR == 1 { print $2 }')
case $java_version in
1.*) java_version=${java_version#1.} ;;
esac
java_major=${java_version%%.*}
case $java_major in
''|*[!0-9]*) return 1 ;;
*) printf '%s\n' "$java_major" ;;
esac
}
install_codegraph() {
if ! command -v curl >/dev/null 2>&1; then
warn "安装 CodeGraph 需要 curl。"
return 1
fi
curl -fsSL "$CODEGRAPH_INSTALL_URL" | sh || return 1
# 官方安装器默认使用 ~/.local/bin;让本次检查立即看到新命令。
if [ -d "$HOME/.local/bin" ]; then
PATH="$HOME/.local/bin:$PATH"
export PATH
fi
command -v codegraph >/dev/null 2>&1
}
install_kotlin_lsp() {
require_brew || return 1
brew install JetBrains/utils/kotlin-lsp || return 1
command -v kotlin-lsp >/dev/null 2>&1
}
install_java_21() {
require_brew || return 1
brew install openjdk@21 || return 1
java_home=$(brew --prefix openjdk@21 2>/dev/null) || return 1
PATH="$java_home/bin:$PATH"
export PATH
installed_java_major=$(java_major_version || true)
if [ -z "$installed_java_major" ] || [ "$installed_java_major" -lt 21 ]; then
return 1
fi
say "已在本次安装流程中使用 $java_home/bin。若新终端仍找不到 Java,请把该目录加入 PATH。"
}
install_jdtls() {
require_brew || return 1
brew install jdtls || return 1
command -v jdtls >/dev/null 2>&1
}
detect_kitty() {
kitty_bin=
if command -v kitty >/dev/null 2>&1; then
kitty_bin=$(command -v kitty)
return 0
fi
for candidate in /Applications/kitty.app/Contents/MacOS/kitty "$HOME/.local/kitty.app/bin/kitty"; do
if [ -x "$candidate" ]; then
kitty_bin=$candidate
return 0
fi
done
return 1
}
install_kitty() {
case $(uname -s) in
Darwin)
require_brew || return 1
brew install --cask kitty || return 1
;;
*)
if ! command -v curl >/dev/null 2>&1; then
warn "安装 Kitty 需要 curl。"
return 1
fi
curl -L "$KITTY_INSTALL_URL" | sh /dev/stdin || return 1
;;
esac
detect_kitty
}
resolve_kitty_config_dir() {
kitty_config_dir=${KITTY_CONFIG_DIRECTORY:-${XDG_CONFIG_HOME:-$HOME/.config}/kitty}
}
kitty_solarized_dark_configured() {
resolve_kitty_config_dir
[ -f "$kitty_config_dir/current-theme.conf" ] || return 1
[ -f "$kitty_config_dir/kitty.conf" ] || return 1
grep -F "Solarized Dark" "$kitty_config_dir/current-theme.conf" >/dev/null 2>&1 || return 1
grep -E '^[[:space:]]*include[[:space:]]+([^#[:space:]]*/)?current-theme\.conf([[:space:]]*(#.*)?)?$' \
"$kitty_config_dir/kitty.conf" >/dev/null 2>&1
}
configure_kitty_solarized_dark() {
"$kitty_bin" +kitten themes --reload-in=none "Solarized Dark"
}
resolve_zsh_paths() {
oh_my_zsh_dir=${ZSH:-$HOME/.oh-my-zsh}
zsh_custom_dir=${ZSH_CUSTOM:-$oh_my_zsh_dir/custom}
zshrc_file=${ZDOTDIR:-$HOME}/.zshrc
my_pi_config_dir=${XDG_CONFIG_HOME:-$HOME/.config}/my-pi
p10k_managed_config_file=$my_pi_config_dir/p10k.zsh
}
oh_my_zsh_installed() {
resolve_zsh_paths
[ -f "$oh_my_zsh_dir/oh-my-zsh.sh" ]
}
install_oh_my_zsh() {
if ! command -v curl >/dev/null 2>&1; then
warn "安装 Oh My Zsh 需要 curl。"
return 1
fi
if ! command -v git >/dev/null 2>&1; then
warn "安装 Oh My Zsh 需要 git。"
return 1
fi
resolve_zsh_paths
oh_my_zsh_installer=$(curl -fsSL "$OH_MY_ZSH_INSTALL_URL") || return 1
RUNZSH=no CHSH=no KEEP_ZSHRC=yes ZSH="$oh_my_zsh_dir" \
sh -c "$oh_my_zsh_installer" "" --unattended --keep-zshrc || return 1
oh_my_zsh_installed
}
prepare_zshrc() {
resolve_zsh_paths
zshrc_dir=${zshrc_file%/*}
[ "$zshrc_dir" = "$zshrc_file" ] && zshrc_dir=.
mkdir -p "$zshrc_dir" || return 1
[ -f "$zshrc_file" ] || : > "$zshrc_file" || return 1
}
replace_zsh_managed_block() {
managed_id=$1
managed_placement=$2
managed_anchor=$3
shift 3
managed_content=$(mktemp "${TMPDIR:-/tmp}/my-pi-zsh-block.XXXXXX") || return 1
printf '%s\n' "$@" > "$managed_content"
my_pi_replace_managed_block "$zshrc_file" "$managed_id" "$managed_content" \
"$managed_placement" "$managed_anchor"
managed_status=$?
rm -f "$managed_content"
return "$managed_status"
}
zshrc_has_oh_my_zsh_loader() {
[ -f "$zshrc_file" ] || return 1
grep -E '^[[:space:]]*(source|\.)[[:space:]].*oh-my-zsh\.sh' "$zshrc_file" >/dev/null 2>&1
}
ensure_oh_my_zsh_loader() {
prepare_zshrc || return 1
if zshrc_has_oh_my_zsh_loader; then
return 0
fi
replace_zsh_managed_block "oh-my-zsh-loader" "append" "" \
"export ZSH=\"$oh_my_zsh_dir\"" \
"source \"\$ZSH/oh-my-zsh.sh\""
}
set_powerlevel10k_theme() {
prepare_zshrc || return 1
replace_zsh_managed_block "powerlevel10k-theme" "before" \
'^(# >>> my-pi:oh-my-zsh-loader >>>|[[:space:]]*(source|\.)[[:space:]].*oh-my-zsh\.sh)' \
'ZSH_THEME="powerlevel10k/powerlevel10k"'
}
p10k_config_sourced() {
my_pi_managed_block_exists "$zshrc_file" "powerlevel10k-config"
}
ensure_p10k_config_source() {
prepare_zshrc || return 1
replace_zsh_managed_block "powerlevel10k-config" "append" "" \
"[[ ! -f \"$p10k_managed_config_file\" ]] || source \"$p10k_managed_config_file\""
}
configure_powerlevel10k() {
resolve_zsh_paths
powerlevel10k_dir=$zsh_custom_dir/themes/powerlevel10k
if [ ! -f "$powerlevel10k_dir/powerlevel10k.zsh-theme" ]; then
if ! command -v git >/dev/null 2>&1; then
warn "安装 Powerlevel10k 需要 git。"
return 1
fi
mkdir -p "$zsh_custom_dir/themes" || return 1
git clone --depth=1 "$POWERLEVEL10K_REPOSITORY" "$powerlevel10k_dir" || return 1
fi
my_pi_sync_managed_file "$POWERLEVEL10K_PRESET_FILE" "$p10k_managed_config_file" || return 1
set_powerlevel10k_theme || return 1
ensure_oh_my_zsh_loader || return 1
ensure_p10k_config_source || return 1
say "已应用仓库内置的 Powerlevel10k Rainbow/ASCII 受管配置。"
}
zsh_plugin_enabled() {
plugin=$1
[ -f "$zshrc_file" ] || return 1
awk -v target="$plugin" '
{
line = $0
sub(/[[:space:]]*#.*/, "", line)
if (line ~ /^[[:space:]]*plugins[[:space:]]*[+]?=/) active = 1
if (active) {
gsub(/[()=+]/, " ", line)
count = split(line, fields, /[[:space:]]+/)
for (i = 1; i <= count; i++) if (fields[i] == target) found = 1
if ($0 ~ /\)/) active = 0
}
}
END { exit(found ? 0 : 1) }
' "$zshrc_file"
}
add_zsh_plugin() {
plugin=$1
prepare_zshrc || return 1
ensure_oh_my_zsh_loader || return 1
replace_zsh_managed_block "zsh-plugin-$plugin" "before" \
'^(# >>> my-pi:oh-my-zsh-loader >>>|[[:space:]]*(source|\.)[[:space:]].*oh-my-zsh\.sh)' \
"plugins+=($plugin)"
}
install_and_enable_zsh_plugin() {
plugin=$1
repository=${2:-}
if [ -n "$repository" ] && [ ! -d "$zsh_custom_dir/plugins/$plugin" ]; then
if ! command -v git >/dev/null 2>&1; then
warn "安装 $plugin 需要 git。"
return 1
fi
mkdir -p "$zsh_custom_dir/plugins" || return 1
git clone --depth=1 "$repository" "$zsh_custom_dir/plugins/$plugin" || return 1
fi
add_zsh_plugin "$plugin"
}
check_zsh_plugin() {
plugin=$1
description=$2
repository=${3:-}
if zsh_plugin_enabled "$plugin"; then
say "✓ $plugin 已启用"
elif ask_to_enable "$description"; then
install_and_enable_zsh_plugin "$plugin" "$repository" || record_install_failure "$plugin"
else
say "- 已跳过 ${plugin}。"
fi
}
if ! command -v pi >/dev/null 2>&1; then
warn "找不到 pi,无法安装组合包。请先安装 Pi 并确保 pi 位于 PATH。"
exit 127
fi
say "正在安装 Pi 组合包:$PACKAGE_SOURCE"
if ! pi install "$PACKAGE_SOURCE"; then
warn "pi install 执行失败,已停止后续依赖检查。"
exit 1
fi
say "Pi 组合包安装完成,开始部署受管配置。"
rebuild_bundle_config || record_install_failure "组合包配置重建"
say "开始检查终端环境和机器级 CLI 依赖。"
kitty_ok=0
if detect_kitty; then
kitty_ok=1
say "✓ Kitty 已安装(${kitty_bin})"
elif ask_to_install "Kitty 终端"; then
if install_kitty; then
kitty_ok=1
say "✓ Kitty 安装完成"
else
record_install_failure "Kitty"
fi
else
say "- 已跳过 Kitty;不会检查 Solarized Dark 主题。"
fi
if [ "$kitty_ok" -eq 1 ]; then
if kitty_solarized_dark_configured; then
say "✓ Kitty 已配置 Solarized Dark"
elif ask_yes_no "Kitty 尚未配置 Solarized Dark。是否现在配置?"; then
configure_kitty_solarized_dark || record_install_failure "Kitty Solarized Dark 主题"
else
say "- 已跳过 Kitty Solarized Dark 主题。"
fi
fi
oh_my_zsh_ok=0
if oh_my_zsh_installed; then
oh_my_zsh_ok=1
say "✓ Oh My Zsh 已安装(${oh_my_zsh_dir})"
elif ask_to_install "Oh My Zsh"; then
if install_oh_my_zsh; then
oh_my_zsh_ok=1
say "✓ Oh My Zsh 安装完成"
else
record_install_failure "Oh My Zsh"
fi
else
say "- 已跳过 Oh My Zsh;不会检查 Powerlevel10k 和 Zsh 插件。"
fi
if [ "$oh_my_zsh_ok" -eq 1 ]; then
resolve_zsh_paths
powerlevel10k_dir=$zsh_custom_dir/themes/powerlevel10k
if [ -f "$powerlevel10k_dir/powerlevel10k.zsh-theme" ] && \
my_pi_managed_block_exists "$zshrc_file" "powerlevel10k-theme" && \
[ -f "$p10k_managed_config_file" ] && \
cmp -s "$POWERLEVEL10K_PRESET_FILE" "$p10k_managed_config_file" && p10k_config_sourced; then
say "✓ Powerlevel10k 已安装并配置"
elif ask_yes_no "Powerlevel10k 尚未完整配置。是否安装并应用仓库内置的 Rainbow/ASCII 配置?"; then
configure_powerlevel10k || record_install_failure "Powerlevel10k"
else
say "- 已跳过 Powerlevel10k。"
fi
check_zsh_plugin "git" "git 插件" ""
check_zsh_plugin "zsh-autosuggestions" "zsh-autosuggestions(命令自动建议)" \
"$ZSH_AUTOSUGGESTIONS_REPOSITORY"
check_zsh_plugin "zsh-syntax-highlighting" "zsh-syntax-highlighting(语法高亮)" \
"$ZSH_SYNTAX_HIGHLIGHTING_REPOSITORY"
fi
if command -v codegraph >/dev/null 2>&1; then
say "✓ codegraph 已安装"
elif ask_to_install "CodeGraph(提供 codegraph_explore)"; then
install_codegraph || record_install_failure "CodeGraph"
else
say "- 已跳过 CodeGraph;codegraph_explore 将不可用。"
fi
if command -v kotlin-lsp >/dev/null 2>&1; then
say "✓ kotlin-lsp 已安装"
elif ask_to_install "kotlin-lsp(Kotlin LSP)"; then
install_kotlin_lsp || record_install_failure "kotlin-lsp"
else
say "- 已跳过 kotlin-lsp;Kotlin LSP 功能将不可用。"
fi
java_ok=0
if command -v java >/dev/null 2>&1; then
java_major=$(java_major_version || true)
if [ -n "$java_major" ] && [ "$java_major" -ge 21 ]; then
java_ok=1
say "✓ Java $java_major 已安装(满足 Java 21+)"
fi
fi
if [ "$java_ok" -eq 0 ]; then
if ask_to_install "Java 21+(JDT LS 运行时)"; then
if install_java_21; then
java_ok=1
else
record_install_failure "Java 21+"
fi
else
say "- 已跳过 Java 21+;JDT LS 可能无法启动。"
fi
fi
if command -v jdtls >/dev/null 2>&1; then
say "✓ jdtls 已安装"
elif ask_to_install "jdtls(Java LSP)"; then
install_jdtls || record_install_failure "jdtls"
else
say "- 已跳过 jdtls;Java LSP 功能将不可用。"
fi
say "终端环境与依赖检查完成。RTK 命令改写默认关闭,因此未检查可选的 rtk CLI。"
exit "$install_failed"