Files
my-pi/update.sh
T
云服务部-叶林立 cdd91b3f8b feat: 移除 Codex Fast mode 与 AutoReview 后台分析,模型改为配置驱动
移除 pi-extension-codex-fast-mode:
- 删除整个扩展目录;同步清理根 package.json 的 files / pi.extensions /
  dependencies 条目、package-lock.json、tests/extension-load.test.ts 断言
  与文档章节。
- pi-minimal-footer 的 Fast on 渲染是该扩展的唯一消费者。扩展移除后 accent
  分支不再有生产者,collectFooterStatusItems 简化为直接返回非空状态字符串,
  去掉 FooterStatusItem 与 accent 标志,避免留下死代码。

移除 500 次裁决后的后台分析体系:
- 删除 analysis-agent.ts(192 行)与 ledger.ts(296 行),以及对应的
  auto-review-runtime / auto-review-ledger 测试。
- runtime.ts 由 177 行收敛到 85 行:安装选项缩为 { service, config };删除
  handleAgentSettled 与 agent_settled 注册;保留 session_start / turn_start
  (denial circuit breaker 属安全语义)/ session_shutdown。
- 删除 analysis 配置键:config、extension-config、config-schema、生成的
  permissions.schema.json 与种子配置。
- 删除 reviewer 的 onDecision 回调(唯一消费者是 ledger)。
- 删除迁移机制 scripts/migrate-permission-policy.mjs、其测试与 update.sh 中
  46 行 migrate_permission_policy 接线。种子部署保持“目标缺失才写入”,已存在
  的 policy.jsonc 永不被覆盖。

AutoReview 模型改为纯配置驱动:
- 删除 DEFAULT_PROVIDER / DEFAULT_MODEL 常量与 findCodexTemplate 合成逻辑;
  model.ts 由 65 行收敛到 48 行。
- provider / model 任一缺失时返回新增失败类别 model-unconfigured 并 defer 到
  人工审批;provider 未注册或模型未发布同样 defer。
- reasoning 不再有默认常量,完全由配置决定。
- session_start 在 auto-review 已列入 authorizerChain 且 provider/model 未配齐
  时 ui.notify 提示一次,避免不使用 AutoReview 的用户被打扰。
- 种子 autoReview 只保留 enabled,provider/model/reasoning 由用户按本机实际
  可用的模型填写。

破坏性变更:analysis 键已从 strictObject schema 删除,任何仍带该键的
policy.jsonc 会使整份配置被拒(fail-closed 到全 ask)。这是刻意的变更,已由
config-loader 测试固定,防止将来无声放宽。

测试:
- 新增 auto-review-model.test.ts(7 例),覆盖此前完全缺失的模型解析:真实
  命中、未配置、provider 未知、模型未发布,以及不再合成替代模型。
- 重写 auto-review-runtime.test.ts(6 例):authorizer 注册、未配置时提示
  一次、已配置 / disabled / 无 UI / 不在 chain 时静默。
- config-loader.test.ts 新增 2 例:带 analysis 的旧配置被拒且 fail-closed、
  autoReview 逐字段合并。
- composition-root.test.ts 的 EXPECTED_HANDLERS 移除 agent_settled。

文档:AGENTS.md、README.md、pi-permission-system 的 README / CHANGELOG /
configuration.md / architecture.md / auto-review UPSTREAM.md。

验证:pi-permission-system 的 typecheck、2577 个测试与 build 全部通过;根 50
个测试通过;schema 重新生成无漂移;新种子通过 schema 校验;5 个脚本 sh -n
通过;git diff --check 无空白问题。未安装 shellcheck;未执行真实 pi install /
update(属外部写操作)。
2026-10-14 08:15:13 -07:00

453 lines
16 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
CODEGRAPH_LATEST_RELEASE_URL=https://github.com/colbymchenry/codegraph/releases/latest
KITTY_INSTALL_URL=https://sw.kovidgoyal.net/kitty/installer.sh
KITTY_VERSION_URL=https://sw.kovidgoyal.net/kitty/current-version.txt
update_failed=0
brew_metadata_refreshed=0
say() {
printf '%s\n' "$*"
}
warn() {
printf '警告: %s\n' "$*" >&2
}
record_update_failure() {
warn "$1 升级或配置同步失败;其余已安装组件仍会继续处理。"
update_failed=1
}
select_updated_bundle_config() {
updated_bundle_dir=
case $PACKAGE_SOURCE in
/*)
updated_bundle_dir=$PACKAGE_SOURCE
;;
./*|../*)
updated_bundle_dir=$(CDPATH=; cd "$PACKAGE_SOURCE" 2>/dev/null && pwd) || updated_bundle_dir=
;;
git:git@bitbucket.org:siakitem/my-pi.git)
updated_bundle_dir=${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/git/bitbucket.org/siakitem/my-pi
;;
esac
if [ -n "$updated_bundle_dir" ] && [ -f "$updated_bundle_dir/config/p10k.zsh" ]; then
POWERLEVEL10K_PRESET_FILE=$updated_bundle_dir/config/p10k.zsh
elif [ ! -f "$POWERLEVEL10K_PRESET_FILE" ]; then
warn "无法定位升级后的组合包配置,找不到 ${POWERLEVEL10K_PRESET_FILE}。"
return 1
else
warn "无法从当前 PI_PACKAGE_SOURCE 推导升级后的包目录,将使用脚本目录中的配置。"
fi
}
recover_incomplete_bundle_update() {
recovery_applicable=0
case $PACKAGE_SOURCE in
git:git@bitbucket.org:siakitem/my-pi.git) ;;
*) return 1 ;;
esac
agent_dir=${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}
recovery_target=$agent_dir/git/bitbucket.org/siakitem/my-pi
recovery_marker=$agent_dir/git/bitbucket.org/siakitem/.my-pi.pi-update-incomplete
if [ ! -f "$recovery_marker" ] || [ -L "$recovery_marker" ]; then
return 1
fi
if ! command -v git >/dev/null 2>&1 || [ ! -d "$recovery_target/.git" ]; then
warn "检测到 Pi Git 升级中断标记,但无法验证已安装 checkout,已跳过自动恢复。"
return 1
fi
recovery_target=$(CDPATH=; cd "$recovery_target" 2>/dev/null && pwd -P) || return 1
recovery_top=$(git -C "$recovery_target" rev-parse --show-toplevel 2>/dev/null) || return 1
recovery_top=$(CDPATH=; cd "$recovery_top" 2>/dev/null && pwd -P) || return 1
recovery_origin=$(git -C "$recovery_target" remote get-url origin 2>/dev/null) || return 1
if [ "$recovery_top" != "$recovery_target" ] || \
[ "$recovery_origin" != "git@bitbucket.org:siakitem/my-pi.git" ]; then
warn "检测到 Pi Git 升级中断标记,但 checkout 身份校验失败,拒绝自动清理。"
return 1
fi
recovery_applicable=1
say "检测到默认组合包的 Pi Git 升级中断,正在有限重试依赖清理。"
recovery_attempt=1
while [ "$recovery_attempt" -le 4 ]; do
if git -C "$recovery_target" clean -fdx; then
say "✓ Pi Git 缓存清理在第 ${recovery_attempt} 次尝试后完成。"
return 0
fi
warn "Pi Git 缓存第 ${recovery_attempt} 次清理未完成。"
recovery_attempt=$((recovery_attempt + 1))
done
warn "Pi Git 缓存清理在 4 次尝试后仍未完成;将交由最后一次 pi update 恢复依赖并报告结果。"
return 1
}
migrate_tool_search_settings() {
settings_file=${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/settings.json
if [ ! -e "$settings_file" ]; then
say "- Pi settings.json 尚不存在;Tool Search 将在下次加载时写入当前默认值。"
return 0
fi
if [ -z "${updated_bundle_dir:-}" ] || [ ! -f "$updated_bundle_dir/scripts/migrate-tool-search-settings.mjs" ]; then
warn "无法定位升级后组合包中的 Tool Search settings.json 迁移辅助脚本,已保留现有配置。"
return 1
fi
migration_helper=$updated_bundle_dir/scripts/migrate-tool-search-settings.mjs
if ! command -v node >/dev/null 2>&1; then
warn "迁移 Tool Search settings.json 需要 Node.js,已保留现有配置。"
return 1
fi
migration_result=$(node "$migration_helper" "$settings_file") || return 1
case $migration_result in
migrated:*)
say "✓ Tool Search 旧版默认配置已迁移到当前组合包默认值。"
say " 原配置备份:${migration_result#migrated:}"
;;
current)
say "✓ Tool Search 用户配置已是当前组合包默认版本。"
;;
custom)
say "- 检测到自定义 Tool Search 用户配置,已完整保留。"
;;
missing-section)
say "- settings.json 尚未包含 Tool Search 配置;下次加载时将补充当前默认值。"
;;
missing)
say "- settings.json 在迁移前已被移除;下次加载时将写入当前默认值。"
;;
*)
warn "Tool Search settings.json 迁移返回未知状态,已停止该项同步。"
return 1
;;
esac
}
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
}
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"
}
update_git_checkout() {
component=$1
checkout=$2
if [ ! -d "$checkout/.git" ]; then
warn "$component 已安装,但不是脚本可安全升级的 Git checkout,已跳过版本升级。"
return 0
fi
if ! command -v git >/dev/null 2>&1; then
warn "升级 $component 需要 git。"
return 1
fi
upstream=$(git -C "$checkout" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null) || {
warn "$component 的 Git checkout 没有可识别的上游分支,已跳过版本升级。"
return 1
}
git -C "$checkout" fetch --quiet || return 1
local_revision=$(git -C "$checkout" rev-parse HEAD) || return 1
remote_revision=$(git -C "$checkout" rev-parse "$upstream") || return 1
if [ "$local_revision" = "$remote_revision" ]; then
say "✓ $component 已是最新版本。"
return 0
fi
say "发现 $component 有新版本,正在快进升级。"
git -C "$checkout" merge --ff-only "$upstream"
}
refresh_brew_metadata() {
if [ "$brew_metadata_refreshed" -eq 1 ]; then
return 0
fi
say "正在刷新 Homebrew 版本信息。"
brew update || return 1
brew_metadata_refreshed=1
}
update_brew_formula_if_installed() {
formula=$1
description=$2
command_name=$3
if command -v brew >/dev/null 2>&1 && brew list --formula "$formula" >/dev/null 2>&1; then
refresh_brew_metadata || {
record_update_failure "$description"
return 0
}
outdated_formula=$(brew outdated --formula --quiet "$formula") || {
record_update_failure "$description"
return 0
}
if [ -z "$outdated_formula" ]; then
say "✓ $description 已是最新版本。"
else
say "发现 $description 有新版本,正在升级。"
brew upgrade "$formula" || record_update_failure "$description"
fi
elif command -v "$command_name" >/dev/null 2>&1; then
say "- $description 已安装,但不由 Homebrew 管理,已保留现有安装。"
else
say "- $description 未安装,升级流程跳过。"
fi
}
detect_kitty() {
kitty_bin=
if command -v kitty >/dev/null 2>&1; then
kitty_bin=$(command -v kitty)
return 0
fi
case $(uname -s) in
Darwin) kitty_candidates="/Applications/kitty.app/Contents/MacOS/kitty" ;;
*) kitty_candidates="$HOME/.local/kitty.app/bin/kitty" ;;
esac
for candidate in $kitty_candidates; do
if [ -x "$candidate" ]; then
kitty_bin=$candidate
return 0
fi
done
return 1
}
resolve_kitty_config_dir() {
kitty_config_dir=${KITTY_CONFIG_DIRECTORY:-${XDG_CONFIG_HOME:-$HOME/.config}/kitty}
}
kitty_uses_solarized_dark() {
resolve_kitty_config_dir
[ -f "$kitty_config_dir/current-theme.conf" ] &&
grep -F "Solarized Dark" "$kitty_config_dir/current-theme.conf" >/dev/null 2>&1
}
update_kitty() {
if ! detect_kitty; then
say "- Kitty 未安装,升级流程跳过。"
return 0
fi
if command -v brew >/dev/null 2>&1 && brew list --cask kitty >/dev/null 2>&1; then
if refresh_brew_metadata; then
if outdated_kitty=$(brew outdated --cask --quiet kitty); then
if [ -z "$outdated_kitty" ]; then
say "✓ Kitty 已是最新版本。"
else
say "发现 Kitty 有新版本,正在升级。"
brew upgrade --cask kitty || record_update_failure "Kitty"
fi
else
record_update_failure "Kitty"
fi
else
record_update_failure "Kitty"
fi
elif [ "$kitty_bin" = "$HOME/.local/kitty.app/bin/kitty" ] || \
[ "$kitty_bin" = "/Applications/kitty.app/Contents/MacOS/kitty" ]; then
if ! command -v curl >/dev/null 2>&1; then
record_update_failure "Kitty(检查版本需要 curl)"
else
kitty_version_output=$("$kitty_bin" --version 2>/dev/null) || kitty_version_output=
case $kitty_version_output in
kitty\ *)
installed_kitty_version=${kitty_version_output#kitty }
installed_kitty_version=${installed_kitty_version%% *}
;;
*) installed_kitty_version= ;;
esac
latest_kitty_version=$(curl -fsSL "$KITTY_VERSION_URL") || latest_kitty_version=
if [ -z "$installed_kitty_version" ] || [ -z "$latest_kitty_version" ]; then
record_update_failure "Kitty 版本检查"
elif [ "$installed_kitty_version" = "$latest_kitty_version" ]; then
say "✓ Kitty $installed_kitty_version 已是最新版本。"
else
say "发现 Kitty ${latest_kitty_version}(当前 ${installed_kitty_version}),正在升级。"
installer_file=$(mktemp "${TMPDIR:-/tmp}/my-pi-kitty.XXXXXX") || return 1
if ! curl -fsSL "$KITTY_INSTALL_URL" -o "$installer_file" || \
! sh "$installer_file" launch=n "installer=version-$latest_kitty_version"; then
record_update_failure "Kitty"
fi
rm -f "$installer_file"
fi
fi
else
say "- Kitty 已安装,但不由 Homebrew 或官方 ~/.local 安装管理,已保留现有安装。"
fi
if kitty_uses_solarized_dark; then
"$kitty_bin" +kitten themes --reload-in=none "Solarized Dark" || \
record_update_failure "Kitty Solarized Dark 主题"
fi
}
update_codegraph() {
if ! command -v codegraph >/dev/null 2>&1; then
say "- CodeGraph 未安装,升级流程跳过。"
return 0
fi
if ! command -v curl >/dev/null 2>&1; then
warn "检查 CodeGraph 版本需要 curl。"
return 1
fi
installed_codegraph_version=$(codegraph --version 2>/dev/null) || installed_codegraph_version=
latest_release_url=$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
"$CODEGRAPH_LATEST_RELEASE_URL") || latest_release_url=
case $latest_release_url in
*/releases/tag/*) latest_codegraph_tag=${latest_release_url##*/} ;;
*) latest_codegraph_tag= ;;
esac
latest_codegraph_version=${latest_codegraph_tag#v}
installed_codegraph_version=${installed_codegraph_version#v}
if [ -z "$installed_codegraph_version" ] || [ -z "$latest_codegraph_version" ]; then
warn "无法比较 CodeGraph 的本地版本与最新发布版本,已跳过替换。"
return 1
fi
if [ "$installed_codegraph_version" = "$latest_codegraph_version" ]; then
say "✓ CodeGraph $installed_codegraph_version 已是最新版本。"
return 0
fi
say "发现 CodeGraph ${latest_codegraph_version}(当前 ${installed_codegraph_version}),正在升级。"
installer_file=$(mktemp "${TMPDIR:-/tmp}/my-pi-codegraph.XXXXXX") || return 1
if ! curl -fsSL "$CODEGRAPH_INSTALL_URL" -o "$installer_file" || \
! CODEGRAPH_VERSION="$latest_codegraph_tag" sh "$installer_file"; then
rm -f "$installer_file"
return 1
fi
rm -f "$installer_file"
}
update_zsh_environment() {
resolve_zsh_paths
if [ ! -f "$oh_my_zsh_dir/oh-my-zsh.sh" ]; then
say "- Oh My Zsh 未安装,Powerlevel10k 和 Zsh 插件升级均跳过。"
return 0
fi
if [ -f "$oh_my_zsh_dir/tools/upgrade.sh" ] && command -v zsh >/dev/null 2>&1; then
ZSH="$oh_my_zsh_dir" zsh "$oh_my_zsh_dir/tools/upgrade.sh" || record_update_failure "Oh My Zsh"
else
update_git_checkout "Oh My Zsh" "$oh_my_zsh_dir" || record_update_failure "Oh My Zsh"
fi
powerlevel10k_dir=$zsh_custom_dir/themes/powerlevel10k
if [ -f "$powerlevel10k_dir/powerlevel10k.zsh-theme" ]; then
update_git_checkout "Powerlevel10k" "$powerlevel10k_dir" || record_update_failure "Powerlevel10k"
else
say "- Powerlevel10k 未安装,升级流程跳过。"
fi
for plugin in zsh-autosuggestions zsh-syntax-highlighting; do
plugin_dir=$zsh_custom_dir/plugins/$plugin
if [ -d "$plugin_dir" ]; then
update_git_checkout "$plugin" "$plugin_dir" || record_update_failure "$plugin"
else
say "- $plugin 未安装,升级流程跳过。"
fi
done
[ -f "$zshrc_file" ] || return 0
if my_pi_managed_block_exists "$zshrc_file" "oh-my-zsh-loader"; then
replace_zsh_managed_block "oh-my-zsh-loader" "append" "" \
"export ZSH=\"$oh_my_zsh_dir\"" \
"source \"\$ZSH/oh-my-zsh.sh\"" || record_update_failure ".zshrc Oh My Zsh 受管块"
fi
if my_pi_managed_block_exists "$zshrc_file" "powerlevel10k-theme"; then
replace_zsh_managed_block "powerlevel10k-theme" "before" \
'^(# >>> my-pi:oh-my-zsh-loader >>>|[[:space:]]*(source|\.)[[:space:]].*oh-my-zsh\.sh)' \
'ZSH_THEME="powerlevel10k/powerlevel10k"' || record_update_failure ".zshrc Powerlevel10k 主题受管块"
fi
for plugin in git zsh-autosuggestions zsh-syntax-highlighting; do
if my_pi_managed_block_exists "$zshrc_file" "zsh-plugin-$plugin"; then
replace_zsh_managed_block "zsh-plugin-$plugin" "before" \
'^(# >>> my-pi:oh-my-zsh-loader >>>|[[:space:]]*(source|\.)[[:space:]].*oh-my-zsh\.sh)' \
"plugins+=($plugin)" || record_update_failure ".zshrc $plugin 受管块"
fi
done
if [ -f "$p10k_managed_config_file" ] || \
my_pi_managed_block_exists "$zshrc_file" "powerlevel10k-config"; then
if my_pi_sync_managed_file "$POWERLEVEL10K_PRESET_FILE" "$p10k_managed_config_file"; then
replace_zsh_managed_block "powerlevel10k-config" "append" "" \
"[[ ! -f \"$p10k_managed_config_file\" ]] || source \"$p10k_managed_config_file\"" || \
record_update_failure ".zshrc Powerlevel10k 配置受管块"
else
record_update_failure "Powerlevel10k 受管配置"
fi
fi
}
if ! command -v pi >/dev/null 2>&1; then
warn "找不到 pi,无法升级组合包。请确保 pi 位于 PATH。"
exit 127
fi
say "正在升级 Pi 组合包:$PACKAGE_SOURCE"
if pi update "$PACKAGE_SOURCE"; then
:
else
recovery_applicable=0
recovery_cleaned=0
if recover_incomplete_bundle_update; then
recovery_cleaned=1
fi
if [ "$recovery_applicable" -ne 1 ]; then
warn "pi update 执行失败,已停止后续升级。"
exit 1
fi
if [ "$recovery_cleaned" -eq 1 ]; then
say "正在重试 Pi 组合包升级,由 Pi 完成依赖安装并清除中断标记。"
else
warn "正在执行最后一次 pi update,以恢复可能被部分清理的依赖。"
fi
if ! pi update "$PACKAGE_SOURCE"; then
warn "pi update 自动恢复后仍然失败,已停止后续升级。"
exit 1
fi
fi
select_updated_bundle_config || exit 1
migrate_tool_search_settings || record_update_failure "Tool Search 用户配置"
say "组合包升级完成,开始升级已安装的终端环境和机器级 CLI;未安装项将直接跳过。"
update_kitty || record_update_failure "Kitty"
update_zsh_environment || record_update_failure "Zsh 环境"
update_codegraph || record_update_failure "CodeGraph"
update_brew_formula_if_installed "kotlin-lsp" "kotlin-lsp" "kotlin-lsp"
update_brew_formula_if_installed "openjdk@21" "Java 21" "java"
update_brew_formula_if_installed "jdtls" "jdtls" "jdtls"
say "已安装组件的升级与受管配置同步完成。"
exit "$update_failed"