diff --git a/AGENTS.md b/AGENTS.md index ddd2ac8b..ca29e92f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ - **构词**:`Astr-`(希腊语 *astron*,星星/天体)+ `-ion`(粒子/状态后缀,如 photon、electron)。 - **意象**:星之子、星际粒子、来自宇宙尘埃的微小发光体。 - **寓意**:Agent 像星际间传递信号的粒子,把人类意图传递到工具、文件、终端与子智能体之间;同时把零散任务像星尘聚合成恒星一样,聚合成可执行结果。 -- **状态**:暂定名,后续若变更需同步更新 `AGENTS.md`、`claude.md` 与项目记忆 `astrion_naming`。 +- **状态**:暂定名,后续若变更需同步更新 `AGENTS.md`(及本地 `claude.md`,未随仓库发布)。 - **中文参考**:阿斯特里恩;简称可考虑 **星子**。 ## 1) 当前项目结构(按代码现状) @@ -35,7 +35,7 @@ - `static/src/components/chat/monitor/MonitorDirector.ts` - `static/src/stores/monitor.ts` - `static/src/components/chat/monitor/*` -- **CLI 相关文档** +- **CLI 相关文档**(本地文档,未随仓库发布) - `docs/cli_ui_display_spec.md`: CLI 显示层设计说明 - `docs/cli_slash_commands_spec.md`: CLI `/` 指令设计说明 - **其他子项目/资源** @@ -110,11 +110,6 @@ - 兼容启动方式:`python web_server.py` - 统一入口:`python main.py`(当前实现会默认进入 Web 启动流程) -> **运行环境边界(重要)**:当前仓库(程序本体)跑在 **8091** 端口;**8092** 端口运行的是另一个本项目的代码副本所启动的智能体程序(即你自己的程序)。任何时候都禁止操作或修改 8092 端口的程序及其数据。两套环境的位置如下: -> -> - **macOS 开发环境**:程序本体(8091)的数据存储在 `/Users/jojo/.astrion/astrion`;你的程序(8092)的数据存储在 `/Users/jojo/.astrion/astrion-clone`。 -> - **Windows 环境**:程序本体(8091)的数据存储在 `C:\Users\KOJO JOTARO\.astrion\astrion`;你的程序(8092)代码位于 `E:\astrion-clone`,数据存储在 `C:\Users\KOJO JOTARO\.astrion\astrion-clone`。 - ### Frontend(根目录) - 安装依赖:`npm install` - 构建:`npm run build --silent 2>&1 | tail -n 5`(默认只看最后 5 行,减少 Vite 构建资源列表等无关输出) @@ -199,7 +194,7 @@ 7. **带文字容器固定高度**:所有含内部文字的选项 / 按钮 / 标签 / 容器固定高度,禁止因文字多少被撑大撑高;过长文字用省略号或内部滚动处理。 8. **窗口设最大尺寸 + 内部滚动**:所有弹窗 / 面板 / 列表设置 `max-height` / `max-width`,超出由内部容器滚动,不顶大整个窗口;滚动条要么隐藏,要么做样式适配,不暴露原生粗滚动条。 9. **实体面板禁止半透明**:实体 UI 容器(对话区 / 侧栏 / 下拉/二级菜单 / 抽屉 / 对话框本体 / 状态条 / tooltip / 输入栏)背景必须不透明,且不加 `backdrop-filter` 磨砂。唯一例外:遮罩层 scrim(`--overlay-scrim`,`position:fixed;inset:0`)和刻意玻璃质感装饰保留半透明。 -10. **主题变体必须与基础定义同文件(禁止跨文件补丁覆盖)**:组件的 dark/light/classic 主题变体写在该组件自己的样式文件里(文件内统一的 `body[data-theme='…'] { … }` 块),禁止把某组件的主题样式写到别的文件去覆盖。新增主题样式前先确认该组件基础定义所在文件;发现存量跨文件覆盖应顺手回迁并删除原覆盖。`!important` 只准用于遮盖第三方库样式,禁止用于压过项目自有定义(出现这种需求说明应该合并定义,而不是加 `!important`)。审计脚本:`python3 cache/style_audit.py` + `python3 cache/theme_diff.py`。 +10. **主题变体必须与基础定义同文件(禁止跨文件补丁覆盖)**:组件的 dark/light/classic 主题变体写在该组件自己的样式文件里(文件内统一的 `body[data-theme='…'] { … }` 块),禁止把某组件的主题样式写到别的文件去覆盖。新增主题样式前先确认该组件基础定义所在文件;发现存量跨文件覆盖应顺手回迁并删除原覆盖。`!important` 只准用于遮盖第三方库样式,禁止用于压过项目自有定义(出现这种需求说明应该合并定义,而不是加 `!important`)。审计脚本:`python3 cache/style_audit.py` + `python3 cache/theme_diff.py`(本地脚本,未随仓库发布)。 11. **大面积色块禁止纯黑/近黑**:任何窗口的 hover、头部、背景等大面积色块,禁止使用纯黑(`#000`)或肉眼近黑(如 `#0a0a0a`/`#0f0f0f` 一档)的颜色,深色模式同样如此。深色下的层次靠中性灰阶拉开:hover 用 `--hover-bg`(深色为白色微量 tint,提亮而非压黑),弹窗/面板头部优先用分隔线区分而非深色衬底(参考 `VersioningDialog.vue` 头部)。纯黑/近黑只允许用于小范围强调渲染(如行内代码衬底等小面积场景)。 ## 5.6) 工具结果显示规范(强制) @@ -290,14 +285,14 @@ AI 执行以下流程时,每一步都要向用户说明在做什么: ## 8) Android App 发布联动要求(重要) -当修改前端并需要发布 Android WebView App 时,必须同步执行以下步骤(依据 `doc/android_app_release_and_update.md`): +当修改前端并需要发布 Android WebView App 时,必须同步执行以下步骤(依据 `doc/android_app_release_and_update.md`,本地文档未随仓库发布): 1. 同时更新版本信息 - `android-webview-app/app/build.gradle.kts`:递增 `versionCode`,更新 `versionName` 2. 同时更新更新说明 - `android-webview-app/APP_CHANGELOG.md`:在顶部新增当前版本说明 3. 完成修改后提醒用户运行上传脚本 - - 在发布流程中运行:`bash /Users/jojo/Desktop/agents/正在修复中/agents/upload_android_apk.sh` + - 在发布流程中运行:`bash ./upload_android_apk.sh`(本地私有脚本,未随仓库发布) > 禁止只改前端代码而不更新版本号/更新说明,否则会导致客户端更新提示与分发信息不一致。 @@ -313,7 +308,7 @@ AI 执行以下流程时,每一步都要向用户说明在做什么: ## 10) 宿主机权限模式与沙箱执行机制(2026-05 更新) -完整说明文档:`docs/host_sandbox_and_permission_model.md` +完整说明文档:`docs/host_sandbox_and_permission_model.md`(本地文档,未随仓库发布) 为避免误解,当前系统有两套独立但叠加的控制: @@ -393,7 +388,7 @@ AI 执行以下流程时,每一步都要向用户说明在做什么: ## 11) 多智能体对话类型(multi-agent conversation type) -> **2026-08 重构**:已废除「多智能体模式」全局状态,多智能体是**对话的不可变属性**(`metadata.multi_agent_mode = true`,创建时确定、不可变)。URL 统一为 `/`(旧 `/multiagent/*` 由前端 bootstrap 重定向到裸路径)。数据目录 `~/.astrion/astrion/host/mutiagents/`(保留原拼写)。完整设计与踩坑记录见项目记忆 `multi_agent_mode_design`,重构方案见 `docs/conversation_type_unification_plan.md`,本节只列 Agent 改代码时必须知道的硬约束。 +> **2026-08 重构**:已废除「多智能体模式」全局状态,多智能体是**对话的不可变属性**(`metadata.multi_agent_mode = true`,创建时确定、不可变)。URL 统一为 `/`(旧 `/multiagent/*` 由前端 bootstrap 重定向到裸路径)。数据目录 `~/.astrion/astrion/host/mutiagents/`(保留原拼写)。重构方案见 `docs/conversation_type_unification_plan.md`(本地设计文档,未随仓库发布),本节只列 Agent 改代码时必须知道的硬约束。 ### 11.0 前端对话类型模型(重构后) @@ -429,7 +424,7 @@ AI 执行以下流程时,每一步都要向用户说明在做什么: - 输出端(`SubAgentTask._forward_output_to_master`):子智能体每次 assistant 文本输出都封为标准格式消息并 `push_master_message` 到会话的 `MultiAgentState.pending_master_messages`,不再做事。 - 接收端(`MultiAgentState` + dispatch):根据主智能体当前状态选择插入方式: - **情况1(主运行中)**:在 `execute_tool_calls` 末尾 `process_multi_agent_master_messages(inline=True, after_tool_call_id=...)` 插入到下一轮模型 messages 列表里(详见 `server/chat_flow_tool_loop.py:1326`)。 - - **情况2(主空闲)**:`poll_multi_agent_notifications` spawn 出后台 poll,主对话空闲且 pool 有消息就 drain,调 `_dispatch_multi_agent_idle_messages` 创建 `task_type="notice"` 的后续 task,触发新一轮工作。完整顺序见项目记忆 multi_agent_mode_design 中的「情况2完整链路」图。 + - **情况2(主空闲)**:`poll_multi_agent_notifications` spawn 出后台 poll,主对话空闲且 pool 有消息就 drain,调 `_dispatch_multi_agent_idle_messages` 创建 `task_type="notice"` 的后续 task,触发新一轮工作。 - **情况3(主最后一轮无工具调用)**:主循环 `if not tool_calls:` 分支调 `process_multi_agent_master_messages(inline=False)` 后 `continue` 继续迭代,Team Leader 在同 task 续跑处理子智能体输出。(实现上与情况2同路径,即情况3通过后续 task 完成情况2)。 **情况2 硬约束(调试踩过坑)**: @@ -517,13 +512,13 @@ AI 执行以下流程时,每一步都要向用户说明在做什么: --- -注:本节按「现有架构 + 多智能体分支」方案描述,与项目记忆 `multi_agent_mode_design` 同步;如两者冲突以代码为准并同步修订本节。 +注:本节按「现有架构 + 多智能体分支」方案描述;如与代码冲突,以代码为准并同步修订本节。 --- ## 12) 对话级主任务门闸与单写者不变量(2026-08-12) -> 事故背景:一个对话并发运行了两个主聊天任务(socketio 用户任务 + 完成通知轮询器派发的通知任务),交叉写入共享 `conversation_history`,产生 `assistant→assistant→tool→tool` 乱序段,最终 API 400 `tool_call_id is not found`、通知永久丢失。完整根因与修复细节见项目记忆 `main_task_gate_and_parallel_universe`。 +> 事故背景:一个对话并发运行了两个主聊天任务(socketio 用户任务 + 完成通知轮询器派发的通知任务),交叉写入共享 `conversation_history`,产生 `assistant→assistant→tool→tool` 乱序段,最终 API 400 `tool_call_id is not found`、通知永久丢失。本节机制即为修复该事故引入。 ### 12.1 单写者不变量(核心约束) diff --git a/README.md b/README.md index 9d71990c..1ce07661 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ ```bash # 1. 克隆 -git clone <仓库地址> +git clone https://github.com/JOJO6618/astrion.git cd astrion # 2. 初始化:创建 venv、安装依赖、运行交互式配置向导 @@ -164,8 +164,8 @@ docker build -f docker/terminal.Dockerfile -t my-agent-shell:latest . | `server.port` / `server.host` | `WEB_SERVER_PORT` / `WEB_SERVER_HOST` | Web 监听 | | `admin.username` | `AGENT_ADMIN_USERNAME` | 管理员用户名 | | `admin.password_hash` | `AGENT_ADMIN_PASSWORD_HASH` | 管理员密码哈希(见「管理员账户」) | -| `admin.secondary_password_hash` | `ADMIN_SECONDARY_PASSWORD_HASH` | 可选:敏感操作二级密码 | -| `secrets.web_secret_key` | `WEB_SECRET_KEY` | Session 签名密钥(随机 hex) | +| `admin.secondary_password_hash` | `ADMIN_SECONDARY_PASSWORD_HASH` | 敏感操作二级密码(**web 模式实际必需**:未配置时管理接口校验恒失败,管理面板功能不可用) | +| `secrets.web_secret_key` | `WEB_SECRET_KEY` | Session 签名密钥(随机 hex;**web 模式强烈建议配置**:未配置时使用临时密钥,重启后所有登录会话失效) | | `secrets.api_token_secret` | `API_TOKEN_SECRET` | API Token 加密密钥(随机 hex) | | `models.default_response_max_tokens` | `AGENT_DEFAULT_RESPONSE_MAX_TOKENS` | 单轮响应 token 全局上限 | | `search.tavily_api_key(_2)` | `AGENT_TAVILY_API_KEY(_2)` | Tavily 搜索 Key(可选) | @@ -214,9 +214,15 @@ docker build -f docker/terminal.Dockerfile -t my-agent-shell:latest . python3 -c "from werkzeug.security import generate_password_hash; print(generate_password_hash('你的密码'))" ``` +管理员登录邮箱自动生成,规则为 `<用户名>@local`(例如用户名为 `admin`,则登录邮箱为 `admin@local`)。 + ### 普通用户(web 模式) -**邀请码注册制**:`/register` 注册必须提供有效邀请码;邀请码由管理员在用户管理中生成(支持次数限制)。 +**邀请码注册制**:`/register` 注册必须提供有效邀请码;邀请码由管理员在管理面板(`/admin/monitor`)的「邀请码」标签页生成(支持次数限制)。 + +> ⚠️ **安全提示** +> - 注册邮箱仅用于登录与查重,**系统不发送验证邮件**——邀请码是注册的唯一实质门槛,请妥善保管。 +> - 新用户的默认权限模式为「无限制」(`unrestricted`,可执行任意终端命令)。多用户或包含不可信用户的场景下,建议用户在个人空间将默认权限模式调整为 `approval` / `readonly`。 ## 项目结构 diff --git a/agentskills/mcp-tool-config/SKILL.md b/agentskills/mcp-tool-config/SKILL.md index 0260dce0..fd30b152 100644 --- a/agentskills/mcp-tool-config/SKILL.md +++ b/agentskills/mcp-tool-config/SKILL.md @@ -3,5 +3,4 @@ name: mcp-tool-config description: 指导模型在宿主机模式下,如何给自己配置新的mcp工具 --- -Just Read This File -skills/mcp-tool-config/mcp-tool-config-guide.md \ No newline at end of file +阅读本目录下的 [mcp-tool-config-guide.md](mcp-tool-config-guide.md),获取完整的 MCP 工具配置指南。 \ No newline at end of file diff --git a/agentskills/mcp-tool-config/mcp-tool-config-guide.md b/agentskills/mcp-tool-config/mcp-tool-config-guide.md index 4fa83f83..507980c7 100644 --- a/agentskills/mcp-tool-config/mcp-tool-config-guide.md +++ b/agentskills/mcp-tool-config/mcp-tool-config-guide.md @@ -1,7 +1,6 @@ # 本项目新增 MCP 工具配置指南 -> 最后更新:2026-04-26 -> 适用目录:`/Users/jojo/Desktop/agents/正在修复中/agents` +> 最后更新:2026-08-22 本文面向后续维护者,说明如何在当前项目里新增并接入一个 MCP 工具(MCP Server)。 @@ -44,7 +43,7 @@ 仓库已有可直接参考的测试服务: -- `/Users/jojo/Desktop/agents/正在修复中/agents/scripts/mcp_calculator_server.py` +- `scripts/mcp_calculator_server.py` 这个示例使用 stdio,支持: @@ -146,11 +145,11 @@ - `MCP_PROTOCOL_VERSION=2025-06-18` - `MCP_DEFAULT_TIMEOUT_SECONDS=25` -(如果你只是个AI,那就去改这个文件) +(AI 维护者请直接改这个文件) -默认配置文件是运行态文件: +默认配置文件是运行态文件(`DATA_DIR` 解析见 `config/paths.py`): -- `/Users/jojo/Desktop/agents/正在修复中/agents/data/mcp_servers.json` +- `~/.astrion/astrion//data/mcp_servers.json` --- @@ -191,12 +190,12 @@ ## 10. 关键代码位置(便于二次开发) -- MCP 配置:`/Users/jojo/Desktop/agents/正在修复中/agents/config/mcp.py` -- 服务注册表:`/Users/jojo/Desktop/agents/正在修复中/agents/modules/mcp_server_registry.py` -- MCP 客户端桥接:`/Users/jojo/Desktop/agents/正在修复中/agents/modules/mcp_client_manager.py` -- 管理 API:`/Users/jojo/Desktop/agents/正在修复中/agents/server/admin.py` +- MCP 配置:`config/mcp.py` +- 服务注册表:`modules/mcp_server_registry.py` +- MCP 客户端桥接:`modules/mcp_client_manager/`(子包) +- 管理 API:`server/admin.py` - 工具定义/执行: - - `/Users/jojo/Desktop/agents/正在修复中/agents/core/main_terminal_parts/tools_definition.py` - - `/Users/jojo/Desktop/agents/正在修复中/agents/core/main_terminal_parts/tools_execution.py` -- 管理端页面:`/Users/jojo/Desktop/agents/正在修复中/agents/static/src/admin/PolicyApp.vue` -- 测试示例服务:`/Users/jojo/Desktop/agents/正在修复中/agents/scripts/mcp_calculator_server.py` + - `core/main_terminal_parts/tools_definition/`(子包,`tools_definition.py` 为兼容入口) + - `core/main_terminal_parts/tools_execution.py` +- 管理端页面:`static/src/admin/PolicyApp.vue` +- 测试示例服务:`scripts/mcp_calculator_server.py` diff --git a/scripts/public_release_excludes.txt b/scripts/public_release_excludes.txt new file mode 100644 index 00000000..c2713881 --- /dev/null +++ b/scripts/public_release_excludes.txt @@ -0,0 +1,11 @@ +# 公开发布排除清单(GitHub public 分支) +# 用途:发布脚本同步 main → public 时,按本清单剔除文件。 +# 语义:清单内文件在私人仓库 main 正常跟踪、正常工作;仅不进入 GitHub 公开仓库。 +# 格式:rsync --exclude-from 兼容模式(每行一条,# 开头为注释)。 +# 维护规则:新增敏感/内部文件时追加一行;净化后可公开发布的条目应移除。 + +# ---- 内部调研笔记(已完成使命,公开后易过时误导)---- +workflow_research/ + +# ---- docs/ 内部文档(分析/方案过程稿;AGENTS.md 中对 docs/ 的引用需随 AGENTS.md 处理一并调整)---- +docs/ diff --git a/scripts/release_public.sh b/scripts/release_public.sh new file mode 100755 index 00000000..d5fb1a56 --- /dev/null +++ b/scripts/release_public.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env bash +# ============================================================================= +# 公开发布脚本:main → GitHub 公开仓库 +# +# 机制: +# 1. 本地 public 分支为 orphan 分支(与 main 无共同历史),防止 main 历史中 +# 曾出现过的敏感文件(如旧版 AGENTS.md)随历史泄漏到公开仓库。 +# 2. 内容来源是 `git archive main`(仅含被跟踪文件),未跟踪文件 +# (.env / node_modules / .astrion/ 等)天然不会混入。 +# 3. 按 scripts/public_release_excludes.txt 剔除「被跟踪但不公开」的条目。 +# 4. 发布前执行泄漏扫描(硬闸),命中即中止。 +# +# 用法: +# bash scripts/release_public.sh # 交互式(摘要后逐确认 commit / push) +# bash scripts/release_public.sh --dry-run # 只导出+扫描+展示摘要,不提交不推送 +# bash scripts/release_public.sh --yes # 跳过确认(仅限已获得明确授权时) +# ============================================================================= + +set -euo pipefail + +# ---- 配置 ------------------------------------------------------------------- +PUBLIC_BRANCH="public" # 本地公开分支(orphan) +GITHUB_REMOTE="github" # GitHub remote 名 +GITHUB_TARGET_BRANCH="main" # 推送到 GitHub 的目标分支 +EXCLUDES_FILE="scripts/public_release_excludes.txt" + +# 泄漏扫描模式(上下文特征,非裸词;避免误报公开信息如 GitHub 用户名) +LEAK_PATTERNS=( + '/Users/jojo' + 'KOJO JOTARO' + 'C:\\Users\\KOJO' + 'cyjai\.com' + 'git\.cyjai' + '正在修复中' + 'astrion-clone' + 'E:\\astrion' +) + +# ---- 参数 ------------------------------------------------------------------- +DRY_RUN=false +ASSUME_YES=false +for arg in "$@"; do + case "$arg" in + --dry-run) DRY_RUN=true ;; + --yes) ASSUME_YES=true ;; + -h|--help) + grep '^#' "$0" | sed 's/^# \{0,1\}//' | head -20 + exit 0 ;; + *) echo "未知参数: $arg"; exit 2 ;; + esac +done + +confirm() { # $1=提示语 + if $ASSUME_YES; then return 0; fi + local reply + read -r -p "$1 [y/N] " reply + [[ "$reply" == "y" || "$reply" == "Y" ]] +} + +# ---- 前置检查 --------------------------------------------------------------- +cd "$(dirname "$0")/.." +echo "==> 仓库根: $(pwd)" + +if ! git remote get-url "$GITHUB_REMOTE" >/dev/null 2>&1; then + echo "!! 未找到 remote '$GITHUB_REMOTE'。请先执行:" + echo " git remote add $GITHUB_REMOTE " + exit 1 +fi + +CURRENT_BRANCH=$(git symbolic-ref --short HEAD) +if [[ "$CURRENT_BRANCH" != "main" ]]; then + echo "!! 当前分支是 $CURRENT_BRANCH,请先切换到 main。" + exit 1 +fi + +MAIN_SHA=$(git rev-parse --short main) +echo "==> 发布来源: main @ ${MAIN_SHA}(未提交到 main 的改动不会包含)" + +# 工作区脏检测:未提交改动不会进入发布内容,明确警告防止「改了忘提交」 +if [[ -n "$(git status --porcelain)" ]]; then + echo "!! 警告:工作区存在未提交改动,以下内容【不会】出现在本次发布中:" + git status --short | head -20 + if ! $DRY_RUN; then + confirm "仍要基于 main 最新提交继续吗?" || { echo "已取消。请先提交或暂存改动。"; exit 1; } + fi +fi + +# ---- 准备 public orphan 分支 ------------------------------------------------ +if ! git show-ref --verify --quiet "refs/heads/$PUBLIC_BRANCH"; then + echo "==> 首次发布:创建 orphan 分支 '$PUBLIC_BRANCH'(空初始提交,与 main 无历史关联)" + EMPTY_TREE=$(git mktree /dev/null 2>&1 || true + git worktree prune >/dev/null 2>&1 || true +} +trap cleanup EXIT + +git worktree add --force "$WT" "$PUBLIC_BRANCH" >/dev/null +echo "==> 临时 worktree: $WT" + +# 清空 worktree 内容(保留 .git),保证被删除的文件能反映为删除 +find "$WT" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} + + +# ---- 导出 main 内容 ---------------------------------------------------------- +git archive main | tar -x -C "$WT" +echo "==> 已从 main 导出被跟踪文件" + +# ---- 按排除清单剔除 ---------------------------------------------------------- +if [[ -f "$EXCLUDES_FILE" ]]; then + while IFS= read -r pattern; do + [[ -z "$pattern" || "$pattern" =~ ^[[:space:]]*# ]] && continue + target="$WT/${pattern%/}" + if [[ -e "$target" ]]; then + rm -rf "$target" + echo "==> 已剔除: $pattern" + fi + done < "$EXCLUDES_FILE" +else + echo "!! 警告:排除清单 $EXCLUDES_FILE 不存在" +fi + +# ---- 泄漏扫描(硬闸) -------------------------------------------------------- +echo "==> 执行泄漏扫描..." +LEAK_HIT=false +for pattern in "${LEAK_PATTERNS[@]}"; do + hits=$(grep -rInE --exclude-dir=.git --exclude=.git --exclude=release_public.sh -- "$pattern" "$WT" 2>/dev/null || true) + if [[ -n "$hits" ]]; then + LEAK_HIT=true + echo "!! 命中模式 [$pattern]:" + echo "$hits" | sed "s|$WT/||" | head -10 + fi +done +if $LEAK_HIT; then + echo "!! 泄漏扫描未通过,已中止。请净化后重试,或确认误报后调整扫描模式。" + exit 1 +fi +echo "==> 泄漏扫描通过" + +# ---- 摘要 -------------------------------------------------------------------- +cd "$WT" +git add -A +echo +echo "==================== 变更摘要(相对上一次公开提交) ====================" +git status --short | head -60 +CHANGED=$(git status --short | wc -l | tr -d ' ') +TOTAL=$(find . -type f ! -path './.git/*' | wc -l | tr -d ' ') +echo "--------------------------------------------------------------------" +echo "变更条目: $CHANGED 公开文件总数: $TOTAL" +echo "======================================================================" +echo + +if $DRY_RUN; then + echo "==> dry-run 结束(未提交、未推送)。临时 worktree 已自动清理。" + exit 0 +fi + +if [[ "$CHANGED" == "0" ]]; then + echo "==> 与上一次公开提交相比无变化。" + confirm "仍然创建一个空发布提交并推送吗?" || { echo "已取消。"; exit 0; } +fi + +# ---- 提交 -------------------------------------------------------------------- +DATE=$(date +%Y-%m-%d) +echo "==> 提交信息: release: sync from main @$MAIN_SHA ($DATE)" +confirm "确认在 '$PUBLIC_BRANCH' 分支上创建发布提交?" || { echo "已取消。"; exit 0; } +git commit -m "release: sync from main @$MAIN_SHA ($DATE)" + +# ---- 推送 -------------------------------------------------------------------- +echo "==> 推送目标: $GITHUB_REMOTE $PUBLIC_BRANCH:$GITHUB_TARGET_BRANCH" +confirm "确认推送到 GitHub(公开仓库)?" || { echo "已取消推送(本地提交已保留)。 "; exit 0; } +git push "$GITHUB_REMOTE" "$PUBLIC_BRANCH:$GITHUB_TARGET_BRANCH" + +echo "==> 完成。公开仓库已更新到 main @$MAIN_SHA 的快照。"