trantor 0.17.60 → 0.17.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/bin/cli.mjs +2 -0
- package/bin/crew.sh +174 -8
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trantor",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.61",
|
|
4
4
|
"description": "Trantor — the hub-world for AI agent crews: live message bus, presence, project Kanban/flow board + crew orchestration for independent AI coding agents (Claude, Codex, Gemini, Kimi, DeepSeek)",
|
|
5
5
|
"mcpServers": {
|
|
6
6
|
"relay": {
|
package/bin/cli.mjs
CHANGED
|
@@ -25,6 +25,7 @@ switch (cmd) {
|
|
|
25
25
|
case "up": process.argv.splice(2, 1); spawn("/bin/bash", [join(ROOT, "bin/crew.sh"), "up", ...args], { stdio: "inherit", cwd: process.cwd() }).on("exit", c => process.exit(c ?? 0)); break;
|
|
26
26
|
case "down": spawn("/bin/bash", [join(ROOT, "bin/crew.sh"), "down", ...args], { stdio: "inherit", cwd: process.cwd() }).on("exit", c => process.exit(c ?? 0)); break;
|
|
27
27
|
case "swap": spawn("/bin/bash", [join(ROOT, "bin/crew.sh"), "swap", ...args], { stdio: "inherit", cwd: process.cwd() }).on("exit", c => process.exit(c ?? 0)); break;
|
|
28
|
+
case "prune": spawn("/bin/bash", [join(ROOT, "bin/crew.sh"), "prune", ...args], { stdio: "inherit", cwd: process.cwd() }).on("exit", c => process.exit(c ?? 0)); break;
|
|
28
29
|
case "hub": {
|
|
29
30
|
const sub = args[0];
|
|
30
31
|
// Per-project hub routing (TDD §12.1): a project lives on exactly ONE hub; codependent
|
|
@@ -167,6 +168,7 @@ switch (cmd) {
|
|
|
167
168
|
trantor watch live bus feed in the terminal
|
|
168
169
|
trantor inbox THIS session's unread bus messages, signed (works under enforce) — [--all] [--consume] [--json]
|
|
169
170
|
trantor policy the autonomy ladder: show | set <project> <1-4> | link <a> <b> --reason "<why>"
|
|
171
|
+
trantor duty the always-on fleet duty agent: up | down | status — hub-escalated triage so you are not the switchboard
|
|
170
172
|
|
|
171
173
|
Claude Code plugin (the orchestrator side):
|
|
172
174
|
claude plugin marketplace add sashabogi/trantor && claude plugin install trantor
|
package/bin/crew.sh
CHANGED
|
@@ -125,6 +125,36 @@ end tell
|
|
|
125
125
|
OSA
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
# surgical STATE row removal: drop every row matching PROJECT+KIND (+AGENT/+HANDLE when given).
|
|
129
|
+
# Empty $3/$4 = wildcard. Callers beware: uses _parse_row, which clobbers the RP/RK/RA/RH globals.
|
|
130
|
+
_state_drop() { # $1=project $2=kind $3=agent(''=any) $4=handle(''=any)
|
|
131
|
+
[ "$DRY" = "1" ] && return 0
|
|
132
|
+
[ -f "$STATE" ] || return 0
|
|
133
|
+
local tmp="$STATE.tmp" line
|
|
134
|
+
: > "$tmp"
|
|
135
|
+
while IFS= read -r line; do
|
|
136
|
+
[ -n "$line" ] || continue
|
|
137
|
+
_parse_row "$line"
|
|
138
|
+
if [ "$RP" = "$1" ] && [ "$RK" = "$2" ] && { [ -z "$3" ] || [ "$RA" = "$3" ]; } && { [ -z "$4" ] || [ "$RH" = "$4" ]; }; then continue; fi
|
|
139
|
+
printf '%s\t%s\t%s\t%s\n' "$RP" "$RK" "$RA" "$RH" >> "$tmp"
|
|
140
|
+
done < "$STATE"
|
|
141
|
+
mv "$tmp" "$STATE"; [ -s "$STATE" ] || rm -f "$STATE"
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Belt-and-suspenders on seat teardown: closing the pane/window SHOULD take the runner with it, but a
|
|
145
|
+
# STALE handle (the 2026-07-30 duplicate-row incident) closes nothing while teardown thinks it's done —
|
|
146
|
+
# the runner survives invisible, still long-polling the inbox. So teardown also kills the seat's
|
|
147
|
+
# processes directly: the per-seat launcher shell, and the runner via the same ANCHORED pattern
|
|
148
|
+
# doctrine as reap_seat (…/<project>$ — a sibling project can never match). CREW_NO_PROC_KILL=1 is the
|
|
149
|
+
# test-suite escape hatch: the suite seeds real project names, and a live crew must survive `npm test`.
|
|
150
|
+
_kill_seat_procs() { # $1=project $2=agent
|
|
151
|
+
[ "${CREW_NO_PROC_KILL:-0}" = "1" ] && return 0
|
|
152
|
+
[ -n "$1" ] && [ -n "$2" ] || return 0
|
|
153
|
+
local pid
|
|
154
|
+
for pid in $(pgrep -f "seats/$1-$2\.sh" 2>/dev/null); do run "kill -9 $pid 2>/dev/null"; done
|
|
155
|
+
for pid in $(pgrep -f "crew-runner\.mjs $2 .*/$1"'$' 2>/dev/null); do run "kill -9 $pid 2>/dev/null"; done
|
|
156
|
+
}
|
|
157
|
+
|
|
128
158
|
# ── down: PROJECT-SCOPED teardown (never touches another project's crew) ─────────────────────────────
|
|
129
159
|
usage_down() {
|
|
130
160
|
cat <<EOF
|
|
@@ -187,6 +217,7 @@ down() {
|
|
|
187
217
|
fi ;;
|
|
188
218
|
win|attach) { [ "${#AGENTS[@]}" -gt 0 ] && [ "$K2" = "attach" ]; } && continue; _kill_win "$H2" ;;
|
|
189
219
|
esac
|
|
220
|
+
case "$K2" in cmuxws|attach) : ;; *) _kill_seat_procs "$P2" "$A2" ;; esac
|
|
190
221
|
done
|
|
191
222
|
|
|
192
223
|
# rewrite STATE minus the rows we tore down (leaves OTHER projects' rows intact)
|
|
@@ -205,13 +236,26 @@ down() {
|
|
|
205
236
|
echo "— crew torn down ($SCOPE_DESC)"
|
|
206
237
|
}
|
|
207
238
|
[ "$CMD" = "down" ] && { down "$@"; exit $?; }
|
|
208
|
-
case "$CMD" in up|swap) ;; *) echo "usage: crew.sh up <agent...> | crew.sh swap <old> <new[:provider[/model]]> | crew.sh down [<agent>...] [--all --yes]"; exit 1 ;; esac
|
|
239
|
+
case "$CMD" in up|swap|prune) ;; *) echo "usage: crew.sh up <agent...> | crew.sh swap <old> <new[:provider[/model]]> | crew.sh down [<agent>...] [--all --yes] | crew.sh prune"; exit 1 ;; esac
|
|
209
240
|
|
|
210
241
|
# self-heal: drop STATE rows whose Terminal window is already gone (dead crews from past sessions), so the
|
|
211
|
-
# file doesn't accumulate ghosts across ups. tmux rows are validated by their session existing
|
|
242
|
+
# file doesn't accumulate ghosts across ups. tmux rows are validated by their session existing; cmux rows
|
|
243
|
+
# by the workspace/surface uuid still existing on the control socket. Socket off (or an empty/unparsable
|
|
244
|
+
# workspace list) ⇒ cmux rows are KEPT — we can't prove death, and they self-heal next time it answers.
|
|
212
245
|
prune_dead_state() {
|
|
213
246
|
[ -f "$STATE" ] || return 0
|
|
214
247
|
[ "$DRY" = "1" ] && return 0
|
|
248
|
+
local CLIVE=""
|
|
249
|
+
if _cmux_ok; then
|
|
250
|
+
local wids wid
|
|
251
|
+
wids="$(_cmux workspace list --id-format both --json 2>/dev/null | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{try{const o=JSON.parse(d.slice(d.search(/[\[{]/)));const a=Array.isArray(o)?o:(o.workspaces||[]);console.log(a.map(x=>x.id).filter(Boolean).join(" "))}catch(e){}})')"
|
|
252
|
+
if [ -n "$wids" ]; then
|
|
253
|
+
CLIVE="$wids"
|
|
254
|
+
for wid in $wids; do
|
|
255
|
+
CLIVE="$CLIVE $(_cmux list-pane-surfaces --workspace "$wid" --id-format uuids --json 2>/dev/null | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{try{const o=JSON.parse(d.slice(d.search(/[\[{]/)));const a=o.surfaces||o.panes||o;console.log((Array.isArray(a)?a:[]).map(s=>s.id||s.surface_id).filter(Boolean).join(" "))}catch(e){}})')"
|
|
256
|
+
done
|
|
257
|
+
fi
|
|
258
|
+
fi
|
|
215
259
|
local tmp="$STATE.tmp" line alive
|
|
216
260
|
: > "$tmp"
|
|
217
261
|
while IFS= read -r line; do
|
|
@@ -222,11 +266,15 @@ prune_dead_state() {
|
|
|
222
266
|
[ -n "$(osascript -e "tell application \"Terminal\" to get id of (first window whose id is $RH)" 2>/dev/null)" ] || alive=0
|
|
223
267
|
elif [ "$RK" = "tmux" ]; then
|
|
224
268
|
tmux has-session -t "trantor:$RP" 2>/dev/null || alive=0
|
|
269
|
+
elif [ "$RK" = "cmuxws" ] || [ "$RK" = "cmux" ]; then
|
|
270
|
+
if [ -n "$CLIVE" ]; then case " $CLIVE " in *" $RH "*) : ;; *) alive=0 ;; esac; fi
|
|
225
271
|
fi
|
|
226
272
|
[ "$alive" = "1" ] && printf '%s\t%s\t%s\t%s\n' "$RP" "$RK" "$RA" "$RH" >> "$tmp"
|
|
227
273
|
done < "$STATE"
|
|
228
274
|
mv "$tmp" "$STATE"; [ -s "$STATE" ] || rm -f "$STATE"
|
|
229
275
|
}
|
|
276
|
+
# `crew.sh prune` — run the self-heal on demand (ops: clean ghost rows without spawning anything).
|
|
277
|
+
[ "$CMD" = "prune" ] && { prune_dead_state; echo "— pruned dead crew rows ($STATE) —"; exit 0; }
|
|
230
278
|
|
|
231
279
|
# --task/--difficulty drive LAZY live-model selection for provider-only specs (agent:provider).
|
|
232
280
|
TASK="code"; DIFF="medium"; _ARGS=()
|
|
@@ -395,17 +443,86 @@ spawn_cmux() { # $@ = specs
|
|
|
395
443
|
echo " ~/.config/cmux/cmux.json (cmux auto-reloads). —"
|
|
396
444
|
spawn_cmux_applescript "$@"; return
|
|
397
445
|
fi
|
|
446
|
+
# ── replace, never stack (workspace edition, 2026-08-07) ──────────────────────────────────────────
|
|
447
|
+
# Every `up` used to create a brand-new workspace — no awareness of a crew already on screen — so
|
|
448
|
+
# repeated ups (and the bus-verify RETRY path, which re-enters spawn_crew) stacked dead
|
|
449
|
+
# trantor:<proj> tabs in the sidebar. Observed six deep on crebral-health. Now the NEWEST tracked
|
|
450
|
+
# workspace for this project is REUSED: each spec becomes a fresh pane inside it, replacing any old
|
|
451
|
+
# pane for the same agent; older stacked workspaces are closed. Rows are read even in DRY mode
|
|
452
|
+
# (read-only) so tests can seed a board.
|
|
453
|
+
local REUSE_WS="" line w
|
|
454
|
+
local stale_ws=()
|
|
455
|
+
if [ -f "$STATE" ]; then
|
|
456
|
+
while IFS= read -r line; do
|
|
457
|
+
[ -n "$line" ] || continue
|
|
458
|
+
_parse_row "$line"
|
|
459
|
+
{ [ "$RP" = "$PROJ" ] && [ "$RK" = "cmuxws" ]; } || continue
|
|
460
|
+
[ -n "$REUSE_WS" ] && stale_ws+=("$REUSE_WS")
|
|
461
|
+
REUSE_WS="$RH"
|
|
462
|
+
done < "$STATE"
|
|
463
|
+
fi
|
|
464
|
+
if [ "${#stale_ws[@]}" -gt 0 ]; then
|
|
465
|
+
for w in "${stale_ws[@]}"; do
|
|
466
|
+
echo " → closing stale stacked crew workspace for $PROJ ($w)"
|
|
467
|
+
_cmux_close_tab "$w"; _state_drop "$PROJ" "cmuxws" "" "$w"
|
|
468
|
+
done
|
|
469
|
+
prune_dead_state # the closed workspaces' seat rows just died with them
|
|
470
|
+
fi
|
|
471
|
+
# Untracked strays: LIVE workspaces named trantor:<proj> that STATE doesn't know (rows lost, or a
|
|
472
|
+
# pre-fix pileup). Adopt one as the reuse target if we have none; close the rest.
|
|
473
|
+
if [ "$DRY" != "1" ]; then
|
|
474
|
+
local named nid
|
|
475
|
+
named="$(_cmux workspace list --id-format both --json 2>/dev/null | WSNAME="trantor:$PROJ" node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{try{const o=JSON.parse(d.slice(d.search(/[\[{]/)));const a=Array.isArray(o)?o:(o.workspaces||[]);console.log(a.filter(x=>x.custom_title===process.env.WSNAME||x.name===process.env.WSNAME).map(x=>x.id).filter(Boolean).join(" "))}catch(e){}})')"
|
|
476
|
+
for nid in $named; do
|
|
477
|
+
[ "$nid" = "$REUSE_WS" ] && continue
|
|
478
|
+
[ -f "$STATE" ] && grep -qF "$nid" "$STATE" && continue # tracked → handled above
|
|
479
|
+
if [ -z "$REUSE_WS" ]; then
|
|
480
|
+
echo " → adopting existing untracked crew workspace for $PROJ ($nid)"
|
|
481
|
+
REUSE_WS="$nid"; record_state "$PROJ" "cmuxws" "__ws__" "$nid"
|
|
482
|
+
else
|
|
483
|
+
echo " → closing stray crew workspace for $PROJ ($nid)"
|
|
484
|
+
_cmux_close_tab "$nid"
|
|
485
|
+
fi
|
|
486
|
+
done
|
|
487
|
+
fi
|
|
398
488
|
# Grid tiling: COLS = ceil(sqrt(N)) → 2 seats side-by-side, 4 = 2×2, 6 = 3×2. Row 0 is built with
|
|
399
489
|
# RIGHT splits off the previous column; each later row splits DOWN from the pane directly above it.
|
|
400
490
|
# Every split TARGETS a recorded surface id (--surface) — never "whatever pane happens to be focused",
|
|
401
|
-
# which is what produced the old staircase layout.
|
|
491
|
+
# which is what produced the old staircase layout. (In REUSE mode a replaced seat splits off its OWN
|
|
492
|
+
# old pane — keeping its spot — and added seats split right off the previous new pane; the fresh-grid
|
|
493
|
+
# math only applies to a fresh workspace.)
|
|
402
494
|
local N=$# COLS=1; while [ $(( COLS * COLS )) -lt "$N" ]; do COLS=$(( COLS + 1 )); done
|
|
403
495
|
local SPEC wsid="" surf="" i=0
|
|
404
496
|
local surfs=()
|
|
497
|
+
[ -n "$REUSE_WS" ] && wsid="$REUSE_WS"
|
|
405
498
|
for SPEC in "$@"; do
|
|
406
499
|
resolve_spec "$SPEC"
|
|
407
500
|
local cmd launcher; cmd="$(RUN_CMD)"; launcher="$(_seat_launcher "$AGENT" "$cmd")"
|
|
408
|
-
if [ "$
|
|
501
|
+
if [ -n "$REUSE_WS" ]; then
|
|
502
|
+
# replace-in-place: split the fresh pane FIRST (targeting the agent's old pane when tracked),
|
|
503
|
+
# then close the old pane — split-first so the workspace can never hit zero surfaces mid-swap.
|
|
504
|
+
local OLD_SURF=""
|
|
505
|
+
if [ -f "$STATE" ]; then
|
|
506
|
+
while IFS= read -r line; do
|
|
507
|
+
[ -n "$line" ] || continue
|
|
508
|
+
_parse_row "$line"
|
|
509
|
+
[ "$RP" = "$PROJ" ] && [ "$RK" = "cmux" ] && [ "$RA" = "$AGENT" ] && OLD_SURF="$RH"
|
|
510
|
+
done < "$STATE"
|
|
511
|
+
fi
|
|
512
|
+
if [ "$DRY" = "1" ]; then
|
|
513
|
+
echo "[dry] cmux: reuse workspace $wsid — new-split for $AGENT${OLD_SURF:+ (replacing $OLD_SURF)}"
|
|
514
|
+
surf="%DRYT$i"
|
|
515
|
+
[ -n "$OLD_SURF" ] && _cmux_close_term "$OLD_SURF"
|
|
516
|
+
else
|
|
517
|
+
local tflag=()
|
|
518
|
+
if [ -n "$OLD_SURF" ]; then tflag=(--surface "$OLD_SURF")
|
|
519
|
+
elif [ "$i" -gt 0 ] && [ -n "${surfs[$(( i - 1 ))]}" ]; then tflag=(--surface "${surfs[$(( i - 1 ))]}"); fi
|
|
520
|
+
surf="$(_cmux new-split right --workspace "$wsid" ${tflag[@]+"${tflag[@]}"} --id-format uuids --json 2>/dev/null | _cmux_surf_json)"
|
|
521
|
+
[ -n "$surf" ] || surf="$(_cmux new-split right --workspace "$wsid" --id-format uuids --json 2>/dev/null | _cmux_surf_json)"
|
|
522
|
+
[ -n "$surf" ] && { _cmux send --surface "$surf" "bash $launcher" >/dev/null 2>&1; _cmux send-key --surface "$surf" enter >/dev/null 2>&1; }
|
|
523
|
+
if [ -n "$OLD_SURF" ]; then _cmux_close_term "$OLD_SURF"; _state_drop "$PROJ" "cmux" "$AGENT" ""; fi
|
|
524
|
+
fi
|
|
525
|
+
elif [ "$i" = "0" ]; then
|
|
409
526
|
if [ "$DRY" = "1" ]; then
|
|
410
527
|
echo "[dry] cmux: new-workspace (cwd $DIR) --command 'bash $launcher' → rename 'trantor:$PROJ'"
|
|
411
528
|
wsid="%DRYWS"; surf="%DRYT0"
|
|
@@ -444,10 +561,54 @@ spawn_cmux_applescript() { # $@ = specs
|
|
|
444
561
|
local N=$# COLS=1; while [ $(( COLS * COLS )) -lt "$N" ]; do COLS=$(( COLS + 1 )); done
|
|
445
562
|
local SPEC tabid="" termid="" i=0
|
|
446
563
|
local terms=()
|
|
564
|
+
# replace, never stack — AppleScript edition. No socket ⇒ no liveness list, so the newest tracked
|
|
565
|
+
# tab is validated by asking cmux for it directly; a dead tracked tab is dropped, older stacked
|
|
566
|
+
# tabs are closed. Reuse then rides the normal split path (i>0) from the first seat.
|
|
567
|
+
local line t REUSE_TAB=""
|
|
568
|
+
local stale_tabs=()
|
|
569
|
+
if [ -f "$STATE" ]; then
|
|
570
|
+
while IFS= read -r line; do
|
|
571
|
+
[ -n "$line" ] || continue
|
|
572
|
+
_parse_row "$line"
|
|
573
|
+
{ [ "$RP" = "$PROJ" ] && [ "$RK" = "cmuxws" ]; } || continue
|
|
574
|
+
[ -n "$REUSE_TAB" ] && stale_tabs+=("$REUSE_TAB")
|
|
575
|
+
REUSE_TAB="$RH"
|
|
576
|
+
done < "$STATE"
|
|
577
|
+
fi
|
|
578
|
+
if [ "${#stale_tabs[@]}" -gt 0 ]; then
|
|
579
|
+
for t in "${stale_tabs[@]}"; do
|
|
580
|
+
echo " → closing stale stacked crew workspace for $PROJ ($t)"
|
|
581
|
+
_cmux_close_tab "$t"; _state_drop "$PROJ" "cmuxws" "" "$t"
|
|
582
|
+
done
|
|
583
|
+
fi
|
|
584
|
+
if [ -n "$REUSE_TAB" ] && [ "$DRY" != "1" ]; then
|
|
585
|
+
local ok; ok="$(osascript 2>/dev/null <<OSA
|
|
586
|
+
tell application "cmux"
|
|
587
|
+
repeat with w in windows
|
|
588
|
+
repeat with tt in tabs of w
|
|
589
|
+
if (id of tt) is "$REUSE_TAB" then return "OK"
|
|
590
|
+
end repeat
|
|
591
|
+
end repeat
|
|
592
|
+
return "ERR"
|
|
593
|
+
end tell
|
|
594
|
+
OSA
|
|
595
|
+
)"
|
|
596
|
+
[ "$ok" = "OK" ] || { _state_drop "$PROJ" "cmuxws" "" "$REUSE_TAB"; _state_drop "$PROJ" "cmux" "" ""; REUSE_TAB=""; }
|
|
597
|
+
fi
|
|
598
|
+
[ -n "$REUSE_TAB" ] && { tabid="$REUSE_TAB"; echo " → reusing existing crew workspace for $PROJ ($tabid)"; }
|
|
447
599
|
for SPEC in "$@"; do
|
|
448
600
|
resolve_spec "$SPEC"
|
|
449
601
|
local cmd launcher; cmd="$(RUN_CMD)"; launcher="$(_seat_launcher "$AGENT" "$cmd")"
|
|
450
|
-
|
|
602
|
+
# In REUSE mode, replace-in-place: split off the agent's old terminal when tracked, close it after.
|
|
603
|
+
local OLD_SURF=""
|
|
604
|
+
if [ -n "$REUSE_TAB" ] && [ -f "$STATE" ]; then
|
|
605
|
+
while IFS= read -r line; do
|
|
606
|
+
[ -n "$line" ] || continue
|
|
607
|
+
_parse_row "$line"
|
|
608
|
+
[ "$RP" = "$PROJ" ] && [ "$RK" = "cmux" ] && [ "$RA" = "$AGENT" ] && OLD_SURF="$RH"
|
|
609
|
+
done < "$STATE"
|
|
610
|
+
fi
|
|
611
|
+
if [ -z "$tabid" ] && [ "$i" = "0" ]; then
|
|
451
612
|
if [ "$DRY" = "1" ]; then
|
|
452
613
|
echo "[dry] cmux(AppleScript): new tab (trantor:$PROJ) + run 'bash $launcher'"; tabid="%DRYTAB"; termid="%DRYT0"
|
|
453
614
|
else
|
|
@@ -470,11 +631,15 @@ OSA
|
|
|
470
631
|
fi
|
|
471
632
|
record_state "$PROJ" "cmuxws" "__ws__" "$tabid"
|
|
472
633
|
else
|
|
473
|
-
local dir target
|
|
474
|
-
if [
|
|
475
|
-
|
|
634
|
+
local dir target=""
|
|
635
|
+
if [ -n "$OLD_SURF" ]; then dir="right"; target="$OLD_SURF" # replace-in-place: keep the seat's spot
|
|
636
|
+
elif [ "$i" -gt 0 ]; then
|
|
637
|
+
if [ $(( i / COLS )) = "0" ]; then dir="right"; target="${terms[$(( i - 1 ))]}"
|
|
638
|
+
else dir="down"; target="${terms[$(( i - COLS ))]}"; fi
|
|
639
|
+
else dir="right"; fi # reuse mode, first seat, nothing tracked → focused terminal
|
|
476
640
|
if [ "$DRY" = "1" ]; then
|
|
477
641
|
echo "[dry] cmux(AppleScript): split $dir from ${target:-<focused>} + run 'bash $launcher'"; termid="%DRYT$i"
|
|
642
|
+
[ -n "$OLD_SURF" ] && _cmux_close_term "$OLD_SURF"
|
|
478
643
|
else
|
|
479
644
|
termid="$(osascript 2>/dev/null <<OSA
|
|
480
645
|
tell application "cmux"
|
|
@@ -497,6 +662,7 @@ tell application "cmux"
|
|
|
497
662
|
end tell
|
|
498
663
|
OSA
|
|
499
664
|
)"
|
|
665
|
+
[ -n "$OLD_SURF" ] && [ -n "$termid" ] && [ "$termid" != "ERR" ] && { _cmux_close_term "$OLD_SURF"; _state_drop "$PROJ" "cmux" "$AGENT" ""; }
|
|
500
666
|
fi
|
|
501
667
|
fi
|
|
502
668
|
terms+=("$termid")
|