chore: 界面和提示语里的产品名也改过来

上一版只改了 app 显示名,Swift 里的字面量还写着旧名 —— 而那批是
**用户直接看得到的**:验收守望的提示头、审批被拦时的理由、
岛上作答回给 Claude 的那句、验收页的空状态。

正文用短名 Vibe Foreman,app 显示名保持 Vibe Foreman Free:
全名带 Free 夹在句子中间很别扭,而且以后真出付费版时正文不用再改一遍。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
wuguanganandClaude Opus 5 committed 2026-08-14 22:51:42 +08:00
1 parent bae4f9af86
commit 172ae46515
17 files changed
+30 -30

No files matched your search

+4 -4
View File
@@ -6,7 +6,7 @@ import HubProjects
import HubUI
import SwiftUI
// Claude Hub —— 本地 AI 编程 agent 的调度中心。
// Vibe Foreman —— 本地 AI 编程 agent 的调度中心。
//
// 这个 target 刻意做得很薄:只负责启动 NSApplication、装配灵动岛和主窗口、
// 挂托盘图标。所有逻辑都在 HubKit 的各个库里,那些库可以 `swift test`,这里不行。
@@ -266,7 +266,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
item.button?.image = NSImage(
systemSymbolName: "circle.hexagongrid.fill",
accessibilityDescription: "Claude Hub"
accessibilityDescription: "Vibe Foreman"
)
let menu = NSMenu()
@@ -282,7 +282,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
).target = self
menu.addItem(.separator())
menu.addItem(
withTitle: "退出 Claude Hub", action: #selector(quit), keyEquivalent: "q"
withTitle: "退出 Vibe Foreman", action: #selector(quit), keyEquivalent: "q"
).target = self
item.menu = menu
statusItem = item
@@ -337,7 +337,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
backing: .buffered,
defer: false
)
window.title = "Claude Hub"
window.title = "Vibe Foreman"
window.titlebarAppearsTransparent = true
window.contentView = NSHostingView(rootView: root)
window.center()
+1 -1
View File
@@ -200,7 +200,7 @@ public struct HookDecision: Codable, Sendable {
"hookSpecificOutput": [
"hookEventName": "PreToolUse",
"permissionDecision": verdict.rawValue,
"permissionDecisionReason": reason ?? "被 Claude Hub 拦截",
"permissionDecisionReason": reason ?? "被 Vibe Foreman 拦截",
],
]
guard let data = try? JSONSerialization.data(withJSONObject: output) else { return nil }
+2 -2
View File
@@ -32,7 +32,7 @@ public final class HubSocketServer: @unchecked Sendable {
public func start() throws {
// 客户端可能在我们回写决策之前就关掉连接(不等应答的 hook、
// 被 Ctrl-C 掉的 hubctl、旧版二进制)。不忽略 SIGPIPE 的话,
// 那一次 write 会**直接杀掉 Claude Hub** —— 不是报错,是进程没了。
// 那一次 write 会**直接杀掉 Vibe Foreman** —— 不是报错,是进程没了。
UnixSocket.ignoreSIGPIPEProcessWide()
let fd = try UnixSocket.listen(at: path)
@@ -56,7 +56,7 @@ public final class HubSocketServer: @unchecked Sendable {
let clientFd = accept(listenFd, nil, nil)
guard clientFd >= 0 else { return }
// 客户端可能在我们回写之前就关掉连接(不等应答的 hook、被 Ctrl-C 掉的
// hubctl)。不关掉 SIGPIPE 的话那一次 write 会**直接杀掉 Claude Hub**。
// hubctl)。不关掉 SIGPIPE 的话那一次 write 会**直接杀掉 Vibe Foreman**。
UnixSocket.suppressSIGPIPE(fd: clientFd)
workQueue.async { [handler] in
+1 -1
View File
@@ -142,7 +142,7 @@ enum UnixSocket {
///
/// 这不是理论风险,是实测撞到的:Stop 改成需要应答之后,服务端会给 stop
/// 回写决策;而不等应答的客户端(旧版 hubctl、被 Ctrl-C 掉的 hook)
/// 早就把连接关了。于是每一次这样的收工都会**杀掉 Claude Hub**。
/// 早就把连接关了。于是每一次这样的收工都会**杀掉 Vibe Foreman**。
/// 测试套件是直接崩在 signal 13 上才暴露的。
///
/// 用 per-socket 的 `SO_NOSIGPIPE` 而不是全局 `signal(SIGPIPE, SIG_IGN)`:
@@ -202,9 +202,9 @@ public actor AcceptanceAuditor {
\(summary)
</改动概览>
<Claude Hub 直接观测到的被改文件>
<Vibe Foreman 直接观测到的被改文件>
\(touchedBlock)
</Claude Hub 直接观测到的被改文件>
</Vibe Foreman 直接观测到的被改文件>
<代码改动>
\(patch)
+2 -2
View File
@@ -108,7 +108,7 @@ struct AcceptancePane: View {
ContentUnavailableView(
currentPath.map { "\(label(for: $0)) 还没有清单" } ?? "还没有项目",
systemImage: "checklist",
description: Text("在下面手动加一条,或等 Claude Hub 从你的需求里自动拆出来")
description: Text("在下面手动加一条,或等 Vibe Foreman 从你的需求里自动拆出来")
)
.frame(maxHeight: .infinity)
} else {
@@ -356,7 +356,7 @@ struct AcceptancePane: View {
let lines = pending.prefix(8).map { "- \($0.text)" }.joined(separator: "\n")
let more = pending.count > 8 ? "\n(另有 \(pending.count - 8) 项)" : ""
let text = """
【Claude Hub】你自己列的 todo 里这几项还没做完,逐条确认一下现在的状态:
【Vibe Foreman】你自己列的 todo 里这几项还没做完,逐条确认一下现在的状态:
\(lines)\(more)
"""
let reply = self.reply
+1 -1
View File
@@ -408,7 +408,7 @@ public final class AcceptanceStore {
}
return """
【Claude Hub 验收守望】以下要点来自**用户的原始需求和已批准的计划**,\
【Vibe Foreman 验收守望】以下要点来自**用户的原始需求和已批准的计划**,\
不是你自己列的 todo。逐条核对,给结论和证据。
\(lines.joined(separator: "\n"))
+3 -3
View File
@@ -82,7 +82,7 @@ public final class AgentPromptCoordinator {
id: request.id,
decision: HookDecision(
verdict: .deny,
reason: "用户已在 Claude Hub 上选择:\(option.label)。请按这个选择继续,不要重复提问。"
reason: "用户已在 Vibe Foreman 上选择:\(option.label)。请按这个选择继续,不要重复提问。"
)
)
}
@@ -94,7 +94,7 @@ public final class AgentPromptCoordinator {
id: request.id,
decision: HookDecision(
verdict: .deny,
reason: "用户已在 Claude Hub 上作答 —— \(joined)。请按这些选择继续,不要重复提问。"
reason: "用户已在 Vibe Foreman 上作答 —— \(joined)。请按这些选择继续,不要重复提问。"
)
)
}
@@ -113,7 +113,7 @@ public final class AgentPromptCoordinator {
id: request.id,
decision: HookDecision(
verdict: .deny,
reason: "用户在 Claude Hub 上驳回了这份计划,请调整后重新提出。"
reason: "用户在 Vibe Foreman 上驳回了这份计划,请调整后重新提出。"
)
)
}
+2 -2
View File
@@ -171,7 +171,7 @@ public final class ApprovalCoordinator {
guard !Task.isCancelled else { return }
await self?.resolve(
id: request.id,
decision: HookDecision(verdict: .deny, reason: "Claude Hub:60 秒未响应,已自动拒绝"),
decision: HookDecision(verdict: .deny, reason: "Vibe Foreman:60 秒未响应,已自动拒绝"),
verdictLabel: "timeout"
)
}
@@ -195,7 +195,7 @@ public final class ApprovalCoordinator {
public func deny(_ request: ApprovalRequest) {
resolve(
id: request.id,
decision: HookDecision(verdict: .deny, reason: "用户在 Claude Hub 上拒绝了这次操作"),
decision: HookDecision(verdict: .deny, reason: "用户在 Vibe Foreman 上拒绝了这次操作"),
verdictLabel: "deny"
)
}
+1 -1
View File
@@ -14,7 +14,7 @@ import HubProjects
///
/// 用法:
///
/// HUB_DEMO=1 HUB_ISLAND_STATE=hover "/Applications/Claude Hub.app/Contents/MacOS/ClaudeHub"
/// HUB_DEMO=1 HUB_ISLAND_STATE=hover "/Applications/Vibe Foreman.app/Contents/MacOS/ClaudeHub"
///
/// 实现上是往临时目录写一批 `sessions/<PID>.json` 再让 reader 指过去,
/// 而不是在 store 里塞假数据 —— 这样**走的是和生产完全一样的解析路径**,
+1 -1
View File
@@ -630,7 +630,7 @@ public final class HookCoordinator {
// 用户决策超时长,但比 hubctl 的读超时短。
if semaphore.wait(timeout: .now() + HookTimeouts.serverBridge) == .timedOut {
HubLog.ipc.error("审批桥接超时,按拒绝处理")
return HookDecision(verdict: .deny, reason: "Claude Hub 审批超时")
return HookDecision(verdict: .deny, reason: "Vibe Foreman 审批超时")
}
HubLog.ipc.notice("用户决策:\(result.verdict.rawValue, privacy: .public)")
return result
+1 -1
View File
@@ -4,7 +4,7 @@ import SwiftUI
/// 承载灵动岛的窗口。
///
/// 几个设置都不是可选项:
/// - `.borderless` + `.nonactivatingPanel`:点岛不该把 Claude Hub 变成前台 app,
/// - `.borderless` + `.nonactivatingPanel`:点岛不该把 Vibe Foreman 变成前台 app,
/// 否则用户点一下就丢了当前 app 的焦点。
/// - `level = .statusBar`(25):压过 `.mainMenu`(24),这样岛能盖在菜单栏之上。
/// - `.fullScreenAuxiliary`:别的 app 全屏时岛依然可见 —— 你在全屏 IDE 里写代码时
+1 -1
View File
@@ -946,7 +946,7 @@ struct ExpandedContent: View {
private var header: some View {
HStack(spacing: 8) {
Text("Claude Hub")
Text("Vibe Foreman")
.font(IslandTheme.label(13, .semibold))
.foregroundStyle(.white)
Text("\(store.sessions.count) 会话")
+3 -3
View File
@@ -921,14 +921,14 @@ struct SettingsPane: View {
.font(.system(size: 14, weight: .semibold))
Text("""
关掉时 Claude Hub 一条命令都不会执行。打开后也仍有两道限制:\
关掉时 Vibe Foreman 一条命令都不会执行。打开后也仍有两道限制:\
只放行构建/测试类命令(`swift test`、`npm run`、`bash scripts/*` 等),\
含 shell 元字符的一律拒绝;而且**每条命令首次执行前要你在验收页点「允许」**。
""")
.font(.system(size: 11))
.foregroundStyle(.secondary)
Text("这是 Claude Hub 唯一会在你机器上执行命令的功能。")
Text("这是 Vibe Foreman 唯一会在你机器上执行命令的功能。")
.font(.system(size: 11, weight: .medium))
.foregroundStyle(
verifierSettings.enabled ? IslandTheme.waiting : Color.secondary.opacity(0.6)
@@ -974,7 +974,7 @@ struct SettingsPane: View {
}
}
Text("计数从 Claude Hub 启动时开始。刚装完 hook 的话,新开一个会话才会亮。")
Text("计数从 Vibe Foreman 启动时开始。刚装完 hook 的话,新开一个会话才会亮。")
.font(.system(size: 11))
.foregroundStyle(.tertiary)
.padding(.top, 2)
@@ -92,7 +92,7 @@ public final class HubNotificationCenter: NSObject, UNUserNotificationCenterDele
/// app 在前台时也要显示横幅。
///
/// Claude Hub 是 `.accessory` 类型,本来就不"在前台",但用户点开主窗口时
/// Vibe Foreman 是 `.accessory` 类型,本来就不"在前台",但用户点开主窗口时
/// 它会变成活跃 app,这时通知照样得显示 —— 用户要的是知道别的会话怎么样了。
public nonisolated func userNotificationCenter(
_ center: UNUserNotificationCenter,
+1 -1
View File
@@ -311,7 +311,7 @@ public struct TerminalReply {
// MARK: - 留痕
/// 护栏⑤。和审批日志放在一起 —— 两者是同一类东西:
/// "Claude Hub 替我做了什么不可逆的事"。
/// "Vibe Foreman 替我做了什么不可逆的事"。
nonisolated static func log(text: String, sessionId: String, target: String) {
HubLog.jump.notice(
"""
+3 -3
View File
@@ -189,7 +189,7 @@ private func runHook(_ name: String) -> Int32 {
// 它认为这次操作值得拦截,只是决策环节出了问题。这种情况必须拒绝 ——
// 拒绝可恢复(Claude 会告诉用户并可重试),放行不可逆。
emitDecision(
HookDecision(verdict: .deny, reason: "Claude Hub 未能完成审批,已保守拒绝")
HookDecision(verdict: .deny, reason: "Vibe Foreman 未能完成审批,已保守拒绝")
)
case .acknowledged:
@@ -205,10 +205,10 @@ private func runDoctor() -> Int32 {
print("socket 文件存在:\(FileManager.default.fileExists(atPath: path) ? "是" : "否")")
if HubSocketClient.isHubRunning() {
print("连接测试:✓ Claude Hub 正在运行")
print("连接测试:✓ Vibe Foreman 正在运行")
return 0
}
print("连接测试:✗ 连不上(Claude Hub 没运行,hook 会全部放行)")
print("连接测试:✗ 连不上(Vibe Foreman 没运行,hook 会全部放行)")
return 1
}