Skip to content

Commit 7dd6695

Browse files
v0.11.0: Remove duplicate run input + add --kill CLI flag
- TopBar: removed duplicate query/run input (only LiveView has the run input now). LiveView's input is the single source of truth for pipeline execution. - TopBar cleaned up: removed unused state (query, running) and dead functions. - Studio CLI: added explicit --kill flag for force-kill (SIGKILL) on port conflict. Default still kills with SIGTERM; --kill escalates to SIGKILL. --no-kill preserved for skipping entirely.
1 parent 5be0ac8 commit 7dd6695

3 files changed

Lines changed: 28 additions & 74 deletions

File tree

‎cmd/slmcode/cmd_core.go‎

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ func resolveAddr(addr string) (host string, port int) {
160160
}
161161

162162
// killExistingStudio finds and kills any slmcode process listening on the given
163-
// address. Returns true if one was found and killed.
164-
func killExistingStudio(addr string) bool {
163+
// address. If force is true, uses SIGKILL instead of SIGTERM.
164+
func killExistingStudio(addr string, force bool) bool {
165165
_, port := resolveAddr(addr)
166166
if port == 0 {
167167
return false
@@ -197,7 +197,11 @@ func killExistingStudio(addr string) bool {
197197
if err != nil {
198198
continue
199199
}
200-
if err := proc.Signal(syscall.SIGTERM); err == nil {
200+
sig := syscall.SIGTERM
201+
if force {
202+
sig = syscall.SIGKILL
203+
}
204+
if err := proc.Signal(sig); err == nil {
201205
fmt.Println(cli.Warn(fmt.Sprintf("Killed existing slmcode studio (pid %d) on port %d", pid, port)))
202206
killed = true
203207
}
@@ -224,7 +228,8 @@ func nextFreeAddr(addr string) string {
224228
}
225229

226230
func studioCmd() *cobra.Command {
227-
var killExisting bool
231+
var noKill bool
232+
var forceKill bool
228233
var portAuto bool
229234

230235
cmd := &cobra.Command{
@@ -260,17 +265,23 @@ Examples:
260265
fmt.Println(cli.Warn(fmt.Sprintf("Port %s in use → auto-switching to %s", addr, newAddr)))
261266
addr = newAddr
262267
}
263-
} else if !killExisting {
268+
} else if !noKill {
264269
// Default: kill the existing instance.
265-
killExistingStudio(addr)
270+
forceKill := forceKill
271+
killExistingStudio(addr, forceKill)
266272
if portIsBound(addr) {
267-
// Still bound — maybe a non-slmcode process.
268-
fmt.Println(cli.Warn(fmt.Sprintf("Port %s is in use by another process.", addr)))
269-
fmt.Println(cli.Dim(" Use --port-auto to auto-switch, or --no-kill to see the original error."))
270-
return fmt.Errorf("port %s is in use and could not be freed — try --port-auto", addr)
273+
// Still bound — try force kill
274+
if !forceKill {
275+
killExistingStudio(addr, true)
276+
}
277+
if portIsBound(addr) {
278+
fmt.Println(cli.Warn(fmt.Sprintf("Port %s is in use by another process.", addr)))
279+
fmt.Println(cli.Dim(" Use --port-auto to auto-switch, --kill to force-kill, or --no-kill to skip."))
280+
return fmt.Errorf("port %s is in use and could not be freed", addr)
281+
}
271282
}
272283
}
273-
// If --no-kill (killExisting=true but we skipped above) — let ListenAndServe fail naturally.
284+
// If --no-kill — let ListenAndServe fail naturally.
274285
}
275286

276287
uiFS, err := fs.Sub(uiEmbed, "ui")
@@ -293,7 +304,8 @@ Examples:
293304
},
294305
}
295306
cmd.Flags().StringVar(&flagListen, "listen", "", "listen address (default from config)")
296-
cmd.Flags().BoolVar(&killExisting, "no-kill", false, "do NOT auto-kill existing studio on the same port")
307+
cmd.Flags().BoolVar(&noKill, "no-kill", false, "do NOT auto-kill existing studio on the same port")
308+
cmd.Flags().BoolVar(&forceKill, "kill", false, "force-kill existing studio with SIGKILL")
297309
cmd.Flags().BoolVar(&portAuto, "port-auto", false, "auto-switch to next free port if the target is in use")
298310
return cmd
299311
}

‎cmd/slmcode/ui/index.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
99
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
1010
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='6' fill='%237c3aed'/><text x='16' y='22' font-family='system-ui' font-weight='700' font-size='18' fill='white' text-anchor='middle'>SL</text></svg>" />
11-
<script type="module" crossorigin src="/assets/index-uYHsv3xX.js"></script>
12-
<link rel="stylesheet" crossorigin href="/assets/index-CrTmqy1n.css">
11+
<script type="module" crossorigin src="/assets/index-jt7my30Z.js"></script>
12+
<link rel="stylesheet" crossorigin href="/assets/index-jwbpe-zG.css">
1313
</head>
1414
<body class="antialiased">
1515
<div id="root"></div>

‎web/src/components/TopBar.tsx‎

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
11
import { useState, useContext, useRef, useEffect } from 'react';
22
import { useNavigate } from 'react-router-dom';
33
import {
4-
Play,
5-
Square,
64
Moon,
75
Sun,
86
Zap,
97
Settings,
108
ChevronDown,
119
} from 'lucide-react';
1210
import { AppContext } from '@/App';
13-
import { startRun, stopRun, getModels, getAgents, updateConfig } from '@/api/client';
11+
import { getModels, getAgents, updateConfig } from '@/api/client';
1412
import type { AgentSpec, AuthStatus, ModelCost } from '@/types';
1513
import clsx from 'clsx';
1614

1715
export default function TopBar() {
1816
const ctx = useContext(AppContext);
1917
const navigate = useNavigate();
2018

21-
const [query, setQuery] = useState('');
22-
const [running, setRunning] = useState(false);
2319
const [models, setModels] = useState<string[]>([]);
2420
const [modelCosts, setModelCosts] = useState<Record<string, ModelCost>>({});
2521
const [enabledModels, setEnabledModels] = useState<string[]>([]);
@@ -79,31 +75,6 @@ export default function TopBar() {
7975
await handleModelSelect(next);
8076
};
8177

82-
const handleRun = async () => {
83-
const q = query.trim();
84-
if (!q || running) return;
85-
setRunning(true);
86-
try {
87-
await startRun({
88-
query: q,
89-
mode: specialist ? 'specialist' : undefined,
90-
specialist: specialist || undefined,
91-
skills: ctx?.config?.pinned_skills,
92-
});
93-
} catch (e) {
94-
console.error('Run failed:', e);
95-
} finally {
96-
setRunning(false);
97-
}
98-
};
99-
100-
const handleStop = async () => {
101-
try {
102-
await stopRun();
103-
} catch { /* ignore */ }
104-
setRunning(false);
105-
};
106-
10778
const handleModelSelect = async (model: string) => {
10879
setShowModelMenu(false);
10980
try {
@@ -138,36 +109,7 @@ export default function TopBar() {
138109
<span className="text-xs text-gray-400 hidden md:inline">Studio</span>
139110
</button>
140111

141-
<div className="flex-1 max-w-2xl mx-auto">
142-
<div className="relative">
143-
<input
144-
type="text"
145-
value={query}
146-
onChange={(e) => setQuery(e.target.value)}
147-
onKeyDown={(e) => e.key === 'Enter' && handleRun()}
148-
placeholder="What would you like to build? e.g. add JWT auth to the API…"
149-
className="w-full h-9 px-4 pr-10 rounded-lg bg-gray-100 dark:bg-gray-800 border border-transparent
150-
focus:border-brand-500 focus:bg-white dark:focus:bg-gray-900 text-sm
151-
placeholder-gray-400 dark:placeholder-gray-500 transition-all duration-150"
152-
/>
153-
<div className="absolute right-1 top-1/2 -translate-y-1/2 flex items-center gap-1">
154-
{running ? (
155-
<button onClick={handleStop} className="btn-ghost p-1.5 rounded-md text-red-500" title="Stop run">
156-
<Square size={16} fill="currentColor" />
157-
</button>
158-
) : (
159-
<button
160-
onClick={handleRun}
161-
disabled={!query.trim()}
162-
className="btn-ghost p-1.5 rounded-md text-brand-500 disabled:text-gray-400"
163-
title="Run"
164-
>
165-
<Play size={16} fill="currentColor" />
166-
</button>
167-
)}
168-
</div>
169-
</div>
170-
</div>
112+
<div className="flex-1" />
171113

172114
{/* Specialist selector */}
173115
{agents.length > 0 && (

0 commit comments

Comments
 (0)