修复: 缓存原子写与 fork 关系持久化,优化摘要重试

- 原子写: 新增 atomicWriteFile,缓存/收藏/forks 改为临时文件+rename,避免崩溃损坏
- fork 关系: executeFork 记录待绑定项,扫描后按 cwd+时间窗口匹配新会话并持久化
- 摘要重试: 去掉 needDetail 反复刷新逻辑,仅对未生成摘要的会话触发,避免浪费 claude -p 调用
- 脚本转义: pwsh/bash 的 Prompt 回显段转义双引号与 $,防止注入和语法错误
- 输入限制: 工作空间数字输入限制 4 位,避免无限增长
- 品牌统一: 底部版本号文案 绝尘 → U-TABS
This commit is contained in:
lxy
2026-07-01 01:29:01 +08:00
parent baec416df7
commit 45e32193af
6 changed files with 156 additions and 34 deletions
+19 -5
View File
@@ -7,8 +7,9 @@ import (
"path/filepath"
"strconv"
"strings"
"time"
"charm.land/bubbletea/v2"
tea "charm.land/bubbletea/v2"
)
// SessionAction 描述一个 Claude Code 会话启动动作
@@ -45,8 +46,9 @@ func (a SessionAction) pwshScript() string {
b.WriteString(fmt.Sprintf("\n\t\tWrite-Host \"=== %s ===\" -ForegroundColor Cyan", a.Title))
if a.Prompt != "" && a.Name == "" {
// workspace prompt 显示
b.WriteString(fmt.Sprintf("\n\t\tWrite-Host \"Prompt: %s\" -ForegroundColor Yellow", a.Prompt))
// workspace prompt 显示(转义双引号,避免破坏 PowerShell 脚本)
pe := strings.ReplaceAll(a.Prompt, "\"", "`\"\"")
b.WriteString(fmt.Sprintf("\n\t\tWrite-Host \"Prompt: %s\" -ForegroundColor Yellow", pe))
}
b.WriteString(fmt.Sprintf("\n\t\tcd \"%s\"", toWinPath(a.Cwd)))
@@ -74,10 +76,14 @@ func (a SessionAction) bashScript() string {
b.WriteString(fmt.Sprintf(`printf '\033]0;%s\007' && echo "=== %s ==="`, a.Title, a.Title))
if a.Prompt != "" && a.Name == "" {
b.WriteString(fmt.Sprintf(` && echo "Prompt: %s"`, a.Prompt))
// bash: 转义双引号与 $ 反斜杠,避免命令注入/语法错误
pe := strings.ReplaceAll(a.Prompt, "\\", "\\\\")
pe = strings.ReplaceAll(pe, "\"", "\\\"")
pe = strings.ReplaceAll(pe, "$", "\\$")
b.WriteString(fmt.Sprintf(" && echo \"Prompt: %s\"", pe))
}
b.WriteString(fmt.Sprintf(` && cd "%s"`, a.Cwd))
b.WriteString(fmt.Sprintf(" && cd \"%s\"", a.Cwd))
b.WriteString(" && claude")
if a.SessionID != "" {
@@ -202,6 +208,14 @@ func (m *Model) executeFork() (*Model, tea.Cmd) {
m.forkBuf = ""
action.Execute(m)
// 记录待绑定 fork:新会话 ID 由 claude CLI 生成,此刻拿不到,
// 留到下次扫描时按 cwd + 时间窗口匹配
m.pendingForks = append(m.pendingForks, pendingFork{
SourceID: s.ID,
Cwd: s.Cwd,
At: time.Now(),
})
title := s.CustomTitle
if title == "" {
title = s.ID[:8]