Skip to content

Commit 40702e7

Browse files
wonderchookclaude
authored andcommitted
feat: add per-session Claude Code argument customization
Allow users to customize CLI arguments per Claude session via a dialog prompt. Adds a "Customize arguments per session" toggle in Settings that, when enabled, shows an NSAlert before creating each Claude tab. The dialog pre-fills with the global extra args and lets users override them for that specific session. Includes extracted finalizeTabCreation helper to deduplicate post-creation code and a stale-project guard in the async completion path. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent f9e648e commit 40702e7

File tree

2 files changed

+67
-4
lines changed

2 files changed

+67
-4
lines changed

Sources/Window/DeckardWindowController.swift

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ class DeckardWindowController: NSWindowController, NSSplitViewDelegate {
585585

586586
// MARK: - Tab Management (within a project)
587587

588-
func createTabInProject(_ project: ProjectItem, isClaude: Bool, name: String? = nil, sessionIdToResume: String? = nil, tmuxSessionToResume: String? = nil) {
588+
func createTabInProject(_ project: ProjectItem, isClaude: Bool, name: String? = nil, sessionIdToResume: String? = nil, tmuxSessionToResume: String? = nil, extraArgs: String? = nil) {
589589
let surface = TerminalSurface()
590590
let tabName: String
591591
if let name = name {
@@ -614,8 +614,8 @@ class DeckardWindowController: NSWindowController, NSSplitViewDelegate {
614614

615615
let initialInput: String?
616616
if isClaude {
617-
let extraArgs = UserDefaults.standard.string(forKey: "claudeExtraArgs") ?? ""
618-
let extraArgsSuffix = extraArgs.isEmpty ? "" : " \(extraArgs)"
617+
let resolvedArgs = extraArgs ?? UserDefaults.standard.string(forKey: "claudeExtraArgs") ?? ""
618+
let extraArgsSuffix = resolvedArgs.isEmpty ? "" : " \(resolvedArgs)"
619619
var claudeArgs = extraArgsSuffix
620620
if let sessionIdToResume {
621621
let encoded = project.path.claudeProjectDirName
@@ -665,7 +665,29 @@ class DeckardWindowController: NSWindowController, NSSplitViewDelegate {
665665
return
666666
}
667667
let project = projects[selectedProjectIndex]
668-
createTabInProject(project, isClaude: isClaude)
668+
669+
if isClaude && UserDefaults.standard.bool(forKey: "promptForSessionArgs") {
670+
promptForClaudeArgs { [weak self] args in
671+
guard let self else { return }
672+
guard let args else {
673+
// User cancelled
674+
self.isCreatingTab = false
675+
return
676+
}
677+
guard self.projects.contains(where: { $0 === project }) else {
678+
self.isCreatingTab = false
679+
return
680+
}
681+
self.createTabInProject(project, isClaude: true, extraArgs: args)
682+
self.finalizeTabCreation(in: project)
683+
}
684+
} else {
685+
createTabInProject(project, isClaude: isClaude)
686+
finalizeTabCreation(in: project)
687+
}
688+
}
689+
690+
private func finalizeTabCreation(in project: ProjectItem) {
669691
project.selectedTabIndex = project.tabs.count - 1
670692
rebuildTabBar()
671693
rebuildSidebar()
@@ -677,6 +699,33 @@ class DeckardWindowController: NSWindowController, NSSplitViewDelegate {
677699
}
678700
}
679701

702+
private func promptForClaudeArgs(completion: @escaping (String?) -> Void) {
703+
let alert = NSAlert()
704+
alert.messageText = "Claude Code Arguments"
705+
alert.informativeText = "Arguments passed to this session:"
706+
alert.addButton(withTitle: "Start")
707+
alert.addButton(withTitle: "Cancel")
708+
709+
let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 24))
710+
field.font = .monospacedSystemFont(ofSize: 12, weight: .regular)
711+
field.stringValue = UserDefaults.standard.string(forKey: "claudeExtraArgs") ?? ""
712+
field.placeholderString = "--permission-mode auto"
713+
alert.accessoryView = field
714+
715+
guard let window else {
716+
completion(nil)
717+
return
718+
}
719+
720+
alert.beginSheetModal(for: window) { response in
721+
if response == .alertFirstButtonReturn {
722+
completion(field.stringValue)
723+
} else {
724+
completion(nil)
725+
}
726+
}
727+
}
728+
680729
func closeCurrentTab() {
681730
guard let project = currentProject else { return }
682731
let idx = project.selectedTabIndex

Sources/Window/SettingsWindow.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ class SettingsWindowController: NSWindowController, NSToolbarDelegate, NSTextFie
140140
extraArgsHelp.textColor = .secondaryLabelColor
141141
grid.addRow(with: [NSGridCell.emptyContentView, extraArgsHelp])
142142

143+
// Per-session args checkbox
144+
let perSessionCheck = NSButton(checkboxWithTitle: "Customize arguments per session", target: self, action: #selector(perSessionArgsToggled(_:)))
145+
perSessionCheck.state = UserDefaults.standard.bool(forKey: "promptForSessionArgs") ? .on : .off
146+
grid.addRow(with: [NSGridCell.emptyContentView, perSessionCheck])
147+
148+
let perSessionHelp = NSTextField(labelWithString: "Show a dialog to set arguments when creating a new Claude tab.")
149+
perSessionHelp.font = .systemFont(ofSize: 11)
150+
perSessionHelp.textColor = .secondaryLabelColor
151+
grid.addRow(with: [NSGridCell.emptyContentView, perSessionHelp])
152+
143153
// Spacer
144154
let spacer = NSView()
145155
spacer.translatesAutoresizingMaskIntoConstraints = false
@@ -180,6 +190,10 @@ class SettingsWindowController: NSWindowController, NSToolbarDelegate, NSTextFie
180190
return pane
181191
}
182192

193+
@objc private func perSessionArgsToggled(_ sender: NSButton) {
194+
UserDefaults.standard.set(sender.state == .on, forKey: "promptForSessionArgs")
195+
}
196+
183197
@objc private func vibrancyToggled(_ sender: NSButton) {
184198
let enabled = sender.state == .on
185199
UserDefaults.standard.set(enabled, forKey: "sidebarVibrancy")

0 commit comments

Comments
 (0)