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:
1 parent
bae4f9af86
commit
172ae46515
17 files changed
+30
-30
No files matched your search
@@ -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()
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -408,7 +408,7 @@ public final class AcceptanceStore {
|
||||
}
|
||||
|
||||
return """
|
||||
【Claude Hub 验收守望】以下要点来自**用户的原始需求和已批准的计划**,\
|
||||
【Vibe Foreman 验收守望】以下要点来自**用户的原始需求和已批准的计划**,\
|
||||
不是你自己列的 todo。逐条核对,给结论和证据。
|
||||
|
||||
\(lines.joined(separator: "\n"))
|
||||
|
||||
@@ -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 上驳回了这份计划,请调整后重新提出。"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 里塞假数据 —— 这样**走的是和生产完全一样的解析路径**,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 里写代码时
|
||||
|
||||
@@ -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) 会话")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user