Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/desktop/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,8 +505,8 @@ func (a *App) SendUpdateNotification(title, message string) error {

func (a *App) DetectTerminals() string { return a.terminal.DetectTerminals() }
func (a *App) GetTerminalConfig() string { return a.terminal.GetTerminalConfig() }
func (a *App) SaveTerminalConfig(selectedTerminal string, projectDirs []string) error {
return a.terminal.SaveTerminalConfig(selectedTerminal, projectDirs)
func (a *App) SaveTerminalConfig(selectedTerminal string, projectDirs []string, claudeCommand string) error {
return a.terminal.SaveTerminalConfig(selectedTerminal, projectDirs, claudeCommand)
}
func (a *App) AddProjectDir(dir string) error { return a.terminal.AddProjectDir(dir) }
func (a *App) RemoveProjectDir(dir string) error { return a.terminal.RemoveProjectDir(dir) }
Expand Down
2 changes: 2 additions & 0 deletions cmd/desktop/frontend/src/i18n/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ export default {
confirmDelete: 'Are you sure you want to delete this project directory?',
addDirFailed: 'Failed to add directory',
dirExists: 'Directory already exists',
launcherCommand: 'Launcher Command',
launcherCommandHelp: 'Custom CLI command to launch Claude Code, defaults to claude (e.g., hapi)',
// Error messages
errors: {
directory_already_exists: 'Directory already exists'
Expand Down
2 changes: 2 additions & 0 deletions cmd/desktop/frontend/src/i18n/zh-CN.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ export default {
confirmDelete: '确定要删除此项目目录吗?',
addDirFailed: '添加目录失败',
dirExists: '目录已存在',
launcherCommand: '启动命令',
launcherCommandHelp: '自定义 Claude Code 启动命令,默认为 claude(例如:hapi)',
// 错误消息
errors: {
directory_already_exists: '目录已存在'
Expand Down
18 changes: 17 additions & 1 deletion cmd/desktop/frontend/src/modules/terminal.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export function initTerminal() {
window.showTerminalModal = showTerminalModal;
window.closeTerminalModal = closeTerminalModal;
window.onTerminalChange = onTerminalChange;
window.onClaudeCommandChange = onClaudeCommandChange;
window.addProjectDir = addProjectDir;
window.removeProjectDir = removeProjectDir;
window.launchTerminal = launchTerminal;
Expand Down Expand Up @@ -87,6 +88,11 @@ async function loadTerminalConfig() {
if (select && terminalConfig.selectedTerminal) {
select.value = terminalConfig.selectedTerminal;
}
// Update claudeCommand input
const cmdInput = document.getElementById('claudeCommandInput');
if (cmdInput) {
cmdInput.value = terminalConfig.claudeCommand || '';
}
} catch (err) {
console.error('Failed to load terminal config:', err);
}
Expand All @@ -105,12 +111,22 @@ async function onTerminalChange() {
const select = document.getElementById('terminalSelect');
terminalConfig.selectedTerminal = select.value;
try {
await SaveTerminalConfig(terminalConfig.selectedTerminal, terminalConfig.projectDirs);
await SaveTerminalConfig(terminalConfig.selectedTerminal, terminalConfig.projectDirs, terminalConfig.claudeCommand || '');
} catch (err) {
console.error('Failed to save terminal config:', err);
}
}

async function onClaudeCommandChange() {
const cmdInput = document.getElementById('claudeCommandInput');
terminalConfig.claudeCommand = cmdInput ? cmdInput.value.trim() : '';
try {
await SaveTerminalConfig(terminalConfig.selectedTerminal, terminalConfig.projectDirs, terminalConfig.claudeCommand);
} catch (err) {
console.error('Failed to save claude command:', err);
}
}

function renderProjectDirs() {
const container = document.getElementById('projectDirList');
if (!container) return;
Expand Down
6 changes: 6 additions & 0 deletions cmd/desktop/frontend/src/modules/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,12 @@ export function initUI() {
</select>
<small class="form-help" id="terminalSelectHelp">${t('terminal.selectTerminalHelp')}</small>
</div>
<div class="form-group">
<label>${t('terminal.launcherCommand')}</label>
<input type="text" id="claudeCommandInput" placeholder="claude"
oninput="window.onClaudeCommandChange()">
<small class="form-help">${t('terminal.launcherCommandHelp')}</small>
</div>
<div class="form-group">
<label><span class="required">*</span>${t('terminal.projectDirs')}</label>
<small class="form-help">${t('terminal.projectDirsHelp')}</small>
Expand Down
4 changes: 2 additions & 2 deletions cmd/desktop/frontend/wailsjs/go/main/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,8 @@ export function SaveSettings(arg1) {
return window['go']['main']['App']['SaveSettings'](arg1);
}

export function SaveTerminalConfig(arg1, arg2) {
return window['go']['main']['App']['SaveTerminalConfig'](arg1, arg2);
export function SaveTerminalConfig(arg1, arg2, arg3) {
return window['go']['main']['App']['SaveTerminalConfig'](arg1, arg2, arg3);
}

export function SelectDirectory() {
Expand Down
7 changes: 7 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type UpdateConfig struct {
type TerminalConfig struct {
SelectedTerminal string `json:"selectedTerminal"` // Selected terminal ID
ProjectDirs []string `json:"projectDirs"` // Project directories
ClaudeCommand string `json:"claudeCommand"` // Custom launcher command, defaults to "claude"
}

// ProxyConfig represents HTTP proxy configuration
Expand Down Expand Up @@ -357,6 +358,7 @@ func (c *Config) GetTerminal() *TerminalConfig {
return &TerminalConfig{
SelectedTerminal: "cmd",
ProjectDirs: []string{},
ClaudeCommand: "",
}
}
return c.Terminal
Expand Down Expand Up @@ -600,6 +602,7 @@ func LoadFromStorage(storage StorageAdapter) (*Config, error) {
config.Terminal = &TerminalConfig{
SelectedTerminal: "cmd",
ProjectDirs: []string{},
ClaudeCommand: "",
}
if selectedTerminal, err := storage.GetConfig("terminal_selected"); err == nil && selectedTerminal != "" {
config.Terminal.SelectedTerminal = selectedTerminal
Expand All @@ -610,6 +613,9 @@ func LoadFromStorage(storage StorageAdapter) (*Config, error) {
config.Terminal.ProjectDirs = dirs
}
}
if claudeCmd, err := storage.GetConfig("terminal_claudeCommand"); err == nil {
config.Terminal.ClaudeCommand = claudeCmd
}

// Load Proxy config
if proxyURL, err := storage.GetConfig("proxy_url"); err == nil && proxyURL != "" {
Expand Down Expand Up @@ -733,6 +739,7 @@ func (c *Config) SaveToStorage(storage StorageAdapter) error {
if dirsJSON, err := json.Marshal(c.Terminal.ProjectDirs); err == nil {
storage.SetConfig("terminal_projectDirs", string(dirsJSON))
}
storage.SetConfig("terminal_claudeCommand", c.Terminal.ClaudeCommand)
}

// Save Proxy config
Expand Down
17 changes: 10 additions & 7 deletions internal/service/terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ func (t *TerminalService) GetTerminalConfig() string {
}

// SaveTerminalConfig saves the terminal configuration
func (t *TerminalService) SaveTerminalConfig(selectedTerminal string, projectDirs []string) error {
func (t *TerminalService) SaveTerminalConfig(selectedTerminal string, projectDirs []string, claudeCommand string) error {
terminalCfg := &config.TerminalConfig{
SelectedTerminal: selectedTerminal,
ProjectDirs: projectDirs,
ClaudeCommand: claudeCommand,
}
t.config.UpdateTerminal(terminalCfg)

Expand All @@ -51,7 +52,7 @@ func (t *TerminalService) SaveTerminalConfig(selectedTerminal string, projectDir
}
}

logger.Info("Terminal config saved: terminal=%s, dirs=%d", selectedTerminal, len(projectDirs))
logger.Info("Terminal config saved: terminal=%s, dirs=%d, claudeCommand=%s", selectedTerminal, len(projectDirs), claudeCommand)
return nil
}

Expand Down Expand Up @@ -107,9 +108,10 @@ func (t *TerminalService) LaunchTerminal(dir string) error {
if terminalID == "" {
terminalID = "cmd"
}
customCmd := terminalCfg.ClaudeCommand

logger.Info("Launching terminal: %s in %s", terminalID, dir)
return terminal.LaunchTerminal(terminalID, dir)
logger.Info("Launching terminal: %s in %s (cmd=%s)", terminalID, dir, customCmd)
return terminal.LaunchTerminalWithCustomCmd(terminalID, dir, customCmd)
}

// GetSessions returns all sessions for a project directory
Expand Down Expand Up @@ -183,13 +185,14 @@ func (t *TerminalService) LaunchSessionTerminal(dir, sessionID string) error {
if terminalID == "" {
terminalID = "cmd"
}
customCmd := terminalCfg.ClaudeCommand

if sessionID != "" {
logger.Info("Launching terminal with session: %s in %s", sessionID, dir)
logger.Info("Launching terminal with session: %s in %s (cmd=%s)", sessionID, dir, customCmd)
} else {
logger.Info("Launching new terminal in %s", dir)
logger.Info("Launching new terminal in %s (cmd=%s)", dir, customCmd)
}
return terminal.LaunchTerminalWithSession(terminalID, dir, sessionID)
return terminal.LaunchSessionTerminalWithCustomCmd(terminalID, dir, sessionID, customCmd)
}

// LaunchCodexTerminal launches a terminal with Codex
Expand Down
25 changes: 20 additions & 5 deletions internal/terminal/launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,18 @@ func LaunchTerminal(terminalID, dir string) error {

// LaunchTerminalWithSession launches a terminal with optional session resume
func LaunchTerminalWithSession(terminalID, dir, sessionID string) error {
cliCmd := getClaudeCommand(sessionID)
cliCmd := getClaudeCommand(sessionID, "")
return launchTerminalWithCli(terminalID, dir, cliCmd)
}

// LaunchTerminalWithCustomCmd launches a terminal with a custom CLI command
func LaunchTerminalWithCustomCmd(terminalID, dir, customCmd string) error {
return LaunchSessionTerminalWithCustomCmd(terminalID, dir, "", customCmd)
}

// LaunchSessionTerminalWithCustomCmd launches a terminal with a custom CLI command and optional session
func LaunchSessionTerminalWithCustomCmd(terminalID, dir, sessionID, customCmd string) error {
cliCmd := getClaudeCommand(sessionID, customCmd)
return launchTerminalWithCli(terminalID, dir, cliCmd)
}

Expand Down Expand Up @@ -89,10 +100,14 @@ func getShellType(shell string) shellType {

// getClaudeCommand returns the claude command with optional session resume
// On macOS, prepends npm initialization to handle lazy-loaded Node environments (nvm, fnm, etc.)
func getClaudeCommand(sessionID string) string {
cmd := "claude"
func getClaudeCommand(sessionID, customCmd string) string {
base := "claude"
if customCmd != "" {
base = customCmd
}
cmd := base
if sessionID != "" {
cmd = fmt.Sprintf("claude -r %s", shellEscape(sessionID))
cmd = fmt.Sprintf("%s -r %s", base, shellEscape(sessionID))
}
if runtime.GOOS == "darwin" {
// Trigger npm lazy-loading for nvm/fnm environments
Expand Down Expand Up @@ -239,7 +254,7 @@ func buildThirdPartyTerminalCommand(shell, dir, claudeCmd string) string {
}

func buildLaunchCommand(termInfo TerminalInfo, dir, sessionID string) *exec.Cmd {
claudeCmd := getClaudeCommand(sessionID)
claudeCmd := getClaudeCommand(sessionID, "")
return buildLaunchCommandWithCli(termInfo, dir, claudeCmd)
}

Expand Down
Loading