BroV: single Mac target, chat brain = local claude CLI (stream-json, resume)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
maksarsanjeev
2026-10-06 19:49:32 +03:00
parent 53b4f60da1
commit 0f77ec8704
25 changed files with 471 additions and 1007 deletions
+42 -2
View File
@@ -598,6 +598,46 @@ struct SettingsView: View {
// MARK: - Chat section
@ViewBuilder private var chatSection: some View {
GroupBox("Claude Code (your subscription, no API key)") {
VStack(alignment: .leading, spacing: 8) {
HStack {
TextField("Folder the chat runs in", text: $state.chatWorkingDirectory)
.textFieldStyle(.roundedBorder)
Button("Choose…") {
let panel = NSOpenPanel()
panel.canChooseDirectories = true
panel.canChooseFiles = false
panel.allowsMultipleSelection = false
if panel.runModal() == .OK, let url = panel.url {
state.chatWorkingDirectory = url.path
ClaudeCodeCLI.shared.newConversation()
}
}
}
Picker("Model", selection: $state.claudeCodeModel) {
ForEach(ClaudeCodeCLI.models, id: \.id) { m in
Text(m.label).tag(m.id)
}
}
Picker("Permissions", selection: $state.claudePermissionMode) {
Text("Ask in the notch").tag("default")
Text("Accept edits").tag("acceptEdits")
Text("Plan only").tag("plan")
Text("Bypass (dangerous)").tag("bypassPermissions")
}
TextField("Path to claude (empty = auto)", text: $state.claudeBinaryPath)
.textFieldStyle(.roundedBorder)
let found = ClaudeCodeCLI.resolveBinary(custom: state.claudeBinaryPath)
Text(found.map { "✓ Using \($0)" } ?? "✗ claude not found — install Claude Code or set the path.")
.font(.system(size: 11))
.foregroundColor(found == nil ? .red : .secondary)
Text("The chat runs `claude -p` in this folder and keeps one conversation until you start a new one. Your MCP servers, skills and CLAUDE.md come along.")
.font(.system(size: 11))
.foregroundColor(.secondary)
}
.padding(6)
}
GroupBox("Anthropic API") {
VStack(alignment: .leading, spacing: 8) {
SecureField("API key (sk-ant-…)", text: $apiKey)
@@ -705,7 +745,7 @@ struct SettingsView: View {
ollamaURL = ""
state.fetchedProviderModels[.ollama] = nil
state.providerModelFetchError[.ollama] = nil
if state.chatProvider == .ollama { state.chatProvider = .anthropic }
if state.chatProvider == .ollama { state.chatProvider = .claudeCode }
statusMessage = "Ollama disconnected."
}
.buttonStyle(.bordered)
@@ -740,7 +780,7 @@ struct SettingsView: View {
lmstudioURL = ""
state.fetchedProviderModels[.lmstudio] = nil
state.providerModelFetchError[.lmstudio] = nil
if state.chatProvider == .lmstudio { state.chatProvider = .anthropic }
if state.chatProvider == .lmstudio { state.chatProvider = .claudeCode }
statusMessage = "LM Studio disconnected."
}
.buttonStyle(.bordered)