Files
wuguanganandClaude Opus 5 3af92f27cf chore: 仓库改名 vibe-foreman,代码认新旧两个目录名
仓库 gulugulu-cn/claude-hub → gulugulu-cn/vibe-foreman。
GitHub 自动重定向旧地址,6 个 Release 全部保留。

代码里按 ~/Documents/code/claude-hub 找脚本和 projects.yaml 的地方,
改成新旧两个目录名都试 —— 老用户的工作区还叫旧名,新 clone 的是新名,
少认一个的表现是"从界面开项目时项目配置没被补齐",而那是静默的。

**Application Support 下的 claude-hub 目录不改名。** 那是用户数据落盘的
地方(验收清单、盯梢配置、已结束会话名册、置顶、审批日志),
改了等于把现有配置全孤立掉。

530 个测试全过。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 23:05:03 +08:00

91 lines
3.6 KiB
Swift
Raw Permalink 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.
import Foundation
/// 主窗口「新建项目」表单背后的命令模型。
///
/// 真正干活的是 `scripts/new-project.sh`(和 /new-project skill、命令行共用
/// 同一份逻辑)—— app 只负责收集参数、拼命令、展示输出。建仓逻辑只维护一份。
public struct NewProjectCommand: Equatable, Sendable {
public enum Remote: String, CaseIterable, Identifiable, Sendable {
case privateRepo
case publicRepo
case localOnly
public var id: String { rawValue }
public var label: String {
switch self {
case .privateRepo: "GitHub 私有仓"
case .publicRepo: "GitHub 公开仓"
case .localOnly: "只建本地仓"
}
}
}
public var name: String = ""
public var parentDir: String = "~/Documents/code"
public var remote: Remote = .privateRepo
/// 防污染 deny(禁止项目会话写宿主机 ~/.claude/ 的全局面)。
public var applyDenyGuard: Bool = true
public var descriptionText: String = ""
public init() {}
public var trimmedName: String {
name.trimmingCharacters(in: .whitespacesAndNewlines)
}
/// 提交前校验。nil = 可以提交。
public var validationError: String? {
let value = trimmedName
if value.isEmpty { return "项目名不能为空" }
if value.contains(where: { $0 == "/" || $0 == "\\" || $0.isWhitespace }) {
return "项目名不能含空格或斜杠"
}
if value.hasPrefix("-") { return "项目名不能以 - 开头" }
if parentDir.trimmingCharacters(in: .whitespaces).isEmpty { return "父目录不能为空" }
return nil
}
/// 目标路径(展示用)。
public var targetPath: String {
let parent = NSString(string: parentDir).expandingTildeInPath
return "\(parent)/\(trimmedName)"
}
/// 拼给 `/bin/bash <script> ...` 的参数。
public func arguments(scriptPath: String) -> [String] {
var args = [scriptPath, trimmedName, "--dir", parentDir]
switch remote {
case .privateRepo: args.append("--private")
case .publicRepo: args.append("--public")
case .localOnly: args.append("--no-remote")
}
if !applyDenyGuard { args.append("--no-deny") }
let desc = descriptionText.trimmingCharacters(in: .whitespacesAndNewlines)
if !desc.isEmpty { args += ["--desc", desc] }
return args
}
/// 定位 new-project.sh:优先 CLAUDE_HUB_DIR,其次 projects.yaml 所在目录
/// (yaml 就住在 hub 仓库根下),最后默认克隆位置。
public static func scriptURL(near yamlURL: URL?) -> URL {
if let dir = ProcessInfo.processInfo.environment["CLAUDE_HUB_DIR"], !dir.isEmpty {
return URL(fileURLWithPath: NSString(string: dir).expandingTildeInPath)
.appendingPathComponent("scripts/new-project.sh")
}
if let yamlURL {
let candidate = yamlURL.deletingLastPathComponent()
.appendingPathComponent("scripts/new-project.sh")
if FileManager.default.fileExists(atPath: candidate.path) { return candidate }
}
return FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent("Documents/code/vibe-foreman/scripts/new-project.sh")
}
/// GUI app 从 launchd 继承的 PATH 只有系统四件套,gh(/usr/local/bin 或
/// /opt/homebrew/bin)会找不到 —— 跑脚本必须显式补全。
public static let guiPATH =
"/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin"
}