mako QoL update
This commit is contained in:
@@ -90,7 +90,8 @@ eventbox, box, label, scale {
|
|||||||
.battery-icon,
|
.battery-icon,
|
||||||
.battery-percent,
|
.battery-percent,
|
||||||
.time-label,
|
.time-label,
|
||||||
.notification-icon {
|
.notification-icon,
|
||||||
|
.dnd-icon {
|
||||||
color: $fg;
|
color: $fg;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 0 $icon-pad;
|
padding: 0 $icon-pad;
|
||||||
@@ -101,7 +102,8 @@ eventbox, box, label, scale {
|
|||||||
|
|
||||||
.signal-eventbox,
|
.signal-eventbox,
|
||||||
.net-eventbox,
|
.net-eventbox,
|
||||||
.notification-eventbox {
|
.notification-eventbox,
|
||||||
|
.dnd-eventbox {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,11 +70,18 @@
|
|||||||
;; Notifications
|
;; Notifications
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(defpoll dnd-state :interval "1s" "bash ~/.config/eww/scripts/mako-dnd-state")
|
||||||
|
|
||||||
(defwidget notification-widget []
|
(defwidget notification-widget []
|
||||||
(eventbox :class "notification-eventbox"
|
(box :orientation "h" :space-evenly false :spacing gap-tight
|
||||||
:onclick "bash ~/.config/eww/scripts/notification-history-toggle"
|
(eventbox :class "notification-eventbox"
|
||||||
:tooltip "Toggle notification history"
|
:onclick "bash ~/.config/eww/scripts/notification-history-toggle"
|
||||||
(label :text "" :class "notification-icon")))
|
:tooltip "Toggle notification history"
|
||||||
|
(label :text "" :class "notification-icon"))
|
||||||
|
(eventbox :class "notification-eventbox dnd-eventbox"
|
||||||
|
:onclick "bash ~/.config/eww/scripts/mako-dnd-toggle"
|
||||||
|
:tooltip "Toggle do-not-disturb"
|
||||||
|
(label :text {dnd-state} :class "notification-icon dnd-icon"))))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Battery
|
;; Battery
|
||||||
|
|||||||
8
eww/.config/eww/scripts/mako-dnd-state
Executable file
8
eww/.config/eww/scripts/mako-dnd-state
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Print the current do-not-disturb state as a bar icon.
|
||||||
|
# Called by eww as a defpoll.
|
||||||
|
if makoctl mode 2>/dev/null | grep -q dnd; then
|
||||||
|
echo "" # bell-off: do-not-disturb active
|
||||||
|
else
|
||||||
|
echo "" # bell-ring: notifications on
|
||||||
|
fi
|
||||||
3
eww/.config/eww/scripts/mako-dnd-toggle
Executable file
3
eww/.config/eww/scripts/mako-dnd-toggle
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Toggle do-not-disturb mode for mako notifications.
|
||||||
|
makoctl mode -t dnd
|
||||||
@@ -14,6 +14,7 @@ height=300
|
|||||||
margin=5
|
margin=5
|
||||||
padding=8
|
padding=8
|
||||||
icon-path=/usr/share/icons/Papirus-Dark
|
icon-path=/usr/share/icons/Papirus-Dark
|
||||||
|
max-icon-size=48
|
||||||
|
|
||||||
# Layout
|
# Layout
|
||||||
anchor=top-right
|
anchor=top-right
|
||||||
@@ -21,29 +22,66 @@ outer-margin=30,0,0,0
|
|||||||
|
|
||||||
# Behavior
|
# Behavior
|
||||||
default-timeout=6000
|
default-timeout=6000
|
||||||
ignore-timeout=0
|
# Force mako's own timeouts so no notification (critical or not) can hang
|
||||||
max-visible=20
|
# indefinitely, even if the app requests timeout=0 (never).
|
||||||
|
ignore-timeout=1
|
||||||
|
max-visible=5
|
||||||
sort=-time
|
sort=-time
|
||||||
|
group-by=app-name
|
||||||
|
max-history=20
|
||||||
|
|
||||||
# Format - show app name in italics
|
# Progress bar for notifications that report progress (downloads, updates)
|
||||||
format=<b>%s</b> <i>(%a)</i>\n%b
|
progress-color=over #69C3FF
|
||||||
markup=1
|
|
||||||
|
|
||||||
# Mouse bindings - click to focus app, invoke action, and close
|
# Format - plain text (markup=0) so app text with <tag> renders literally.
|
||||||
|
# Priority is shown by a leading icon instead of markup/colors.
|
||||||
|
markup=0
|
||||||
|
format= %s (%a)\n%b
|
||||||
|
|
||||||
|
# Mouse bindings
|
||||||
on-button-left=exec ~/.config/mako/scripts/handle-click $id
|
on-button-left=exec ~/.config/mako/scripts/handle-click $id
|
||||||
on-button-middle=none
|
on-button-middle=none
|
||||||
on-button-right=exec makoctl dismiss -n $id
|
on-button-right=exec makoctl dismiss -n $id
|
||||||
|
on-notify=exec ~/.config/mako/scripts/notify-sound $id
|
||||||
|
|
||||||
# Urgency levels
|
# Urgency levels
|
||||||
[urgency=low]
|
[urgency=low]
|
||||||
background-color=#212a3b
|
background-color=#212a3b
|
||||||
border-color=#405275
|
border-color=#405275
|
||||||
|
format= %s (%a)\n%b
|
||||||
|
|
||||||
[urgency=normal]
|
[urgency=normal]
|
||||||
background-color=#1c2433
|
background-color=#1c2433
|
||||||
border-color=#69C3FF
|
border-color=#69C3FF
|
||||||
|
format= %s (%a)\n%b
|
||||||
|
|
||||||
[urgency=critical]
|
[urgency=critical]
|
||||||
background-color=#1c2433
|
background-color=#1c2433
|
||||||
border-color=#FF738A
|
border-color=#FF738A
|
||||||
default-timeout=0
|
format= %s (%a)\n%b
|
||||||
|
# Was 0 (never) - critical notifications now auto-dismiss after 15s
|
||||||
|
default-timeout=15000
|
||||||
|
|
||||||
|
# Grouped notifications (group-by=app-name) - plain text, icon per urgency
|
||||||
|
[grouped]
|
||||||
|
format= (%g) %s\n%b
|
||||||
|
|
||||||
|
[grouped urgency=low]
|
||||||
|
format= (%g) %s\n%b
|
||||||
|
|
||||||
|
[grouped urgency=critical]
|
||||||
|
format= (%g) %s\n%b
|
||||||
|
|
||||||
|
# Placeholder shown when more notifications exist than max-visible
|
||||||
|
[hidden]
|
||||||
|
format= +%h more
|
||||||
|
|
||||||
|
# Pi agent notifications (sent via the pi QoL extension from wezterm)
|
||||||
|
[app-name=wezterm summary=Pi]
|
||||||
|
border-color=#EACD61
|
||||||
|
background-color=#212a3b
|
||||||
|
|
||||||
|
# Do-not-disturb: makoctl mode -a dnd hides all notifications.
|
||||||
|
# Must stay last so it overrides every style above.
|
||||||
|
[mode=dnd]
|
||||||
|
invisible=1
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ height=300
|
|||||||
margin=5
|
margin=5
|
||||||
padding=8
|
padding=8
|
||||||
icon-path=/usr/share/icons/Papirus-Dark
|
icon-path=/usr/share/icons/Papirus-Dark
|
||||||
|
max-icon-size=48
|
||||||
|
|
||||||
# Layout
|
# Layout
|
||||||
anchor=top-center
|
anchor=top-center
|
||||||
@@ -21,29 +22,67 @@ outer-margin=30,0,0,0
|
|||||||
|
|
||||||
# Behavior
|
# Behavior
|
||||||
default-timeout=6000
|
default-timeout=6000
|
||||||
ignore-timeout=0
|
# Force mako's own timeouts so no notification (critical or not) can hang
|
||||||
max-visible=20
|
# indefinitely, even if the app requests timeout=0 (never).
|
||||||
|
ignore-timeout=1
|
||||||
|
max-visible=5
|
||||||
sort=-time
|
sort=-time
|
||||||
|
group-by=app-name
|
||||||
|
max-history=20
|
||||||
|
|
||||||
# Format - show app name in italics
|
# Progress bar for notifications that report progress (downloads, updates)
|
||||||
format=<b>%s</b> <i>(%a)</i>\n%b
|
progress-color=over #$BORDER
|
||||||
markup=1
|
|
||||||
|
|
||||||
# Mouse bindings - click to focus app, invoke action, and close
|
# Format - plain text (markup=0) so app text with <tag> renders literally.
|
||||||
|
# Priority is shown by a leading icon instead of markup/colors.
|
||||||
|
markup=0
|
||||||
|
format= %s (%a)\n%b
|
||||||
|
|
||||||
|
# Mouse bindings
|
||||||
on-button-left=exec ~/.config/mako/scripts/handle-click $id
|
on-button-left=exec ~/.config/mako/scripts/handle-click $id
|
||||||
on-button-middle=none
|
on-button-middle=none
|
||||||
on-button-right=exec makoctl dismiss -n $id
|
on-button-right=exec makoctl dismiss -n $id
|
||||||
|
on-notify=exec ~/.config/mako/scripts/notify-sound $id
|
||||||
|
|
||||||
# Urgency levels
|
# Urgency levels
|
||||||
[urgency=low]
|
[urgency=low]
|
||||||
background-color=#$BG_BAR
|
background-color=#$BG_BAR
|
||||||
border-color=#$TRACK
|
border-color=#$TRACK
|
||||||
|
format= %s (%a)\n%b
|
||||||
|
|
||||||
[urgency=normal]
|
[urgency=normal]
|
||||||
background-color=#$BG
|
background-color=#$BG
|
||||||
border-color=#$BORDER
|
border-color=#$BORDER
|
||||||
|
format= %s (%a)\n%b
|
||||||
|
|
||||||
[urgency=critical]
|
[urgency=critical]
|
||||||
background-color=#$BG
|
background-color=#$BG
|
||||||
border-color=#$URGENT
|
border-color=#$URGENT
|
||||||
default-timeout=0
|
format= %s (%a)\n%b
|
||||||
|
# Was 0 (never) - critical notifications now auto-dismiss after 15s
|
||||||
|
default-timeout=15000
|
||||||
|
|
||||||
|
# Grouped notifications (group-by=app-name) - plain text, icon per urgency
|
||||||
|
[grouped]
|
||||||
|
format= (%g) %s\n%b
|
||||||
|
|
||||||
|
[grouped urgency=low]
|
||||||
|
format= (%g) %s\n%b
|
||||||
|
|
||||||
|
[grouped urgency=critical]
|
||||||
|
format= (%g) %s\n%b
|
||||||
|
|
||||||
|
# Placeholder shown when more notifications exist than max-visible
|
||||||
|
[hidden]
|
||||||
|
format= +%h more
|
||||||
|
|
||||||
|
# Pi agent notifications (sent via the pi QoL extension from wezterm).
|
||||||
|
# Matches theme accent $accent (#EACD61).
|
||||||
|
[app-name=wezterm summary=Pi]
|
||||||
|
border-color=#EACD61
|
||||||
|
background-color=#$BG_BAR
|
||||||
|
|
||||||
|
# Do-not-disturb: makoctl mode -a dnd hides all notifications.
|
||||||
|
# Must stay last so it overrides every style above.
|
||||||
|
[mode=dnd]
|
||||||
|
invisible=1
|
||||||
|
|||||||
BIN
mako/.config/mako/scripts/Chord.wav
Normal file
BIN
mako/.config/mako/scripts/Chord.wav
Normal file
Binary file not shown.
BIN
mako/.config/mako/scripts/Chord2.wav
Normal file
BIN
mako/.config/mako/scripts/Chord2.wav
Normal file
Binary file not shown.
64
mako/.config/mako/scripts/handle-click
Normal file → Executable file
64
mako/.config/mako/scripts/handle-click
Normal file → Executable file
@@ -1,43 +1,47 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Handle notification click: focus app → invoke action → close
|
# Handle notification click: focus the app that sent it → invoke action → dismiss
|
||||||
# Called by mako with notification ID as parameter
|
# Called by mako with the notification ID as argument
|
||||||
|
|
||||||
ID="$1"
|
ID="$1"
|
||||||
|
[ -z "$ID" ] && exit 0
|
||||||
|
|
||||||
if [ -z "$ID" ]; then
|
# Pull the app name out of makoctl list, scoped to this notification ID
|
||||||
exit 0
|
APP=$(makoctl list 2>/dev/null | awk -v id="$ID" '
|
||||||
fi
|
$1 == "Notification" { if (seen) exit; if ($2 == id) seen = 1; next }
|
||||||
|
seen && /^ App name:/ { sub(/^ App name: /, ""); print; exit }')
|
||||||
|
|
||||||
# Get app name from notification using makoctl
|
# Find a sway window matching the app and focus it.
|
||||||
APPNAME=$(makoctl list | grep -A 2 "^Notification $ID" | grep "App name:" | cut -d: -f2- | xargs)
|
# Exact app_id match first, then X11 class, then partial app_id as fallback.
|
||||||
|
if [ -n "$APP" ]; then
|
||||||
|
TREE=$(swaymsg -t get_tree 2>/dev/null)
|
||||||
|
|
||||||
# Step 1: Focus the application window in Sway (if app name found)
|
CID=$(echo "$TREE" | jq -r --arg app "$APP" '
|
||||||
if [ -n "$APPNAME" ]; then
|
[ .. | objects | select(.type? == "con")
|
||||||
# Get the workspace of the window
|
| select((.app_id? // "") != "")
|
||||||
WORKSPACE=$(swaymsg -t get_tree | jq -r ".. | select(.app_id?==\"$APPNAME\" or .window_properties?.class?==\"$APPNAME\") | .workspace" | head -1)
|
| select((.app_id | ascii_downcase) == ($app | ascii_downcase))
|
||||||
|
| .id ][0] // empty')
|
||||||
|
|
||||||
# If workspace not found, try case-insensitive
|
if [ -z "$CID" ]; then
|
||||||
if [ -z "$WORKSPACE" ] || [ "$WORKSPACE" = "null" ]; then
|
CID=$(echo "$TREE" | jq -r --arg app "$APP" '
|
||||||
WORKSPACE=$(swaymsg -t get_tree | jq -r ".. | select(.app_id?|test(\"$APPNAME\";\"i\") or .window_properties?.class?|test(\"$APPNAME\";\"i\")) | .workspace" | head -1)
|
[ .. | objects | select(.type? == "con")
|
||||||
|
| select((.window_properties?.class? // "") != "")
|
||||||
|
| select((.window_properties.class | ascii_downcase) == ($app | ascii_downcase))
|
||||||
|
| .id ][0] // empty')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Switch to the workspace if found
|
if [ -z "$CID" ]; then
|
||||||
if [ -n "$WORKSPACE" ] && [ "$WORKSPACE" != "null" ]; then
|
CID=$(echo "$TREE" | jq -r --arg app "$APP" '
|
||||||
swaymsg workspace "$WORKSPACE" 2>/dev/null
|
[ .. | objects | select(.type? == "con")
|
||||||
sleep 0.1
|
| select((.app_id? // "") != "")
|
||||||
|
| select((.app_id | ascii_downcase) | contains($app | ascii_downcase))
|
||||||
|
| .id ][0] // empty')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Now focus the window
|
if [ -n "$CID" ]; then
|
||||||
swaymsg "[app_id=\"$APPNAME\"]" focus 2>/dev/null || \
|
swaymsg "[con_id=$CID]" focus >/dev/null 2>&1
|
||||||
swaymsg "[app_id=\"(?i)$APPNAME\"]" focus 2>/dev/null || \
|
fi
|
||||||
swaymsg "[class=\"$APPNAME\"]" focus 2>/dev/null || \
|
|
||||||
swaymsg "[class=\"(?i)$APPNAME\"]" focus 2>/dev/null
|
|
||||||
|
|
||||||
sleep 0.1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 2: Invoke the notification's default action (if any)
|
# Invoke the notification's default action (may open/focus the app), then dismiss
|
||||||
makoctl invoke -n "$ID" 2>/dev/null
|
makoctl invoke -n "$ID" >/dev/null 2>&1
|
||||||
|
makoctl dismiss -n "$ID" >/dev/null 2>&1
|
||||||
# Step 3: Dismiss the notification
|
|
||||||
makoctl dismiss -n "$ID" 2>/dev/null
|
|
||||||
|
|||||||
39
mako/.config/mako/scripts/notify-sound
Executable file
39
mako/.config/mako/scripts/notify-sound
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Play a notification sound, alternating between Chord.wav and Chord2.wav.
|
||||||
|
# Skips: do-not-disturb mode and low-urgency notifications.
|
||||||
|
# Usage: notify-sound <notification-id> (called from mako's on-notify binding)
|
||||||
|
|
||||||
|
ID="${1:-}"
|
||||||
|
|
||||||
|
# Silence while do-not-disturb is active
|
||||||
|
if makoctl mode 2>/dev/null | grep -q dnd; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only play for normal/critical urgency
|
||||||
|
if [ -n "$ID" ]; then
|
||||||
|
urgency=$(makoctl list 2>/dev/null | awk -v id="$ID" '
|
||||||
|
$1 == "Notification" { if (seen) exit; if ($2 == id) seen = 1; next }
|
||||||
|
seen && /^ Urgency:/ { print $2; exit }')
|
||||||
|
if [ "$urgency" = "low" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
SOUND_DIR=~/.config/mako/scripts
|
||||||
|
STATE_FILE="${XDG_CACHE_HOME:-$HOME/.cache}/mako-notify-sound"
|
||||||
|
|
||||||
|
# Alternate between the two sounds on every play
|
||||||
|
if [ -f "$STATE_FILE" ] && [ "$(cat "$STATE_FILE")" = "0" ]; then
|
||||||
|
SOUND="$SOUND_DIR/Chord2.wav"
|
||||||
|
echo "1" > "$STATE_FILE"
|
||||||
|
else
|
||||||
|
SOUND="$SOUND_DIR/Chord.wav"
|
||||||
|
echo "0" > "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v paplay >/dev/null 2>&1; then
|
||||||
|
paplay "$SOUND"
|
||||||
|
elif command -v pw-play >/dev/null 2>&1; then
|
||||||
|
pw-play "$SOUND"
|
||||||
|
fi
|
||||||
BIN
mako/.config/mako/scripts/pling.wav
Normal file
BIN
mako/.config/mako/scripts/pling.wav
Normal file
Binary file not shown.
114
pi/.pi/agent/extensions/subagent-pane-reminder.ts
Normal file
114
pi/.pi/agent/extensions/subagent-pane-reminder.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* Subagent pane lifecycle reminder.
|
||||||
|
*
|
||||||
|
* When the parent agent dispatches to a `pane: true` agent via `subagent`,
|
||||||
|
* this extension floats a cleanup reminder to the parent only:
|
||||||
|
*
|
||||||
|
* - immediately: appended to the `subagent` tool result of that call;
|
||||||
|
* - persistently: a one-line system-prompt note on later turns until the
|
||||||
|
* parent calls `stop_subagent` for that agent (or the session ends).
|
||||||
|
*
|
||||||
|
* Nothing is injected for bg agents (`pane: false`), for `delegate_subagent`
|
||||||
|
* (pane targets are rejected there anyway), for `/agents` commands, or in
|
||||||
|
* sessions that never spawn a pane agent. Other sessions and workflows stay
|
||||||
|
* untouched.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||||
|
import * as fs from "node:fs";
|
||||||
|
import * as path from "node:path";
|
||||||
|
import { homedir } from "node:os";
|
||||||
|
|
||||||
|
interface SubagentInput {
|
||||||
|
agent?: unknown;
|
||||||
|
tasks?: Array<{ agent?: unknown }>;
|
||||||
|
chain?: Array<{ agent?: unknown }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const USER_AGENT_DIRS: string[] = [
|
||||||
|
path.join(homedir(), ".pi", "agent", "agents"),
|
||||||
|
path.join(homedir(), ".claude", "agents"),
|
||||||
|
];
|
||||||
|
|
||||||
|
function projectAgentDirs(cwd: string): string[] {
|
||||||
|
const dirs: string[] = [];
|
||||||
|
let current = path.resolve(cwd);
|
||||||
|
for (;;) {
|
||||||
|
dirs.push(path.join(current, ".pi", "agents"), path.join(current, ".claude", "agents"));
|
||||||
|
const parent = path.dirname(current);
|
||||||
|
if (parent === current) break;
|
||||||
|
current = parent;
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasPaneFrontmatter(text: string): boolean {
|
||||||
|
const block = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||||
|
return block ? /^\s*pane\s*:\s*(?:true|yes|on)\s*$/im.test(block[1]) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPaneAgent(name: string, cwd: string): boolean {
|
||||||
|
for (const dir of [...USER_AGENT_DIRS, ...projectAgentDirs(cwd)]) {
|
||||||
|
for (const ext of [".md", ".markdown"]) {
|
||||||
|
try {
|
||||||
|
if (hasPaneFrontmatter(fs.readFileSync(path.join(dir, `${name}${ext}`), "utf8"))) return true;
|
||||||
|
} catch {
|
||||||
|
// not present in this directory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function agentNamesFrom(input: SubagentInput): string[] {
|
||||||
|
const names = new Set<string>();
|
||||||
|
const add = (value: unknown) => {
|
||||||
|
if (typeof value === "string" && value.trim()) names.add(value.trim());
|
||||||
|
};
|
||||||
|
add(input.agent);
|
||||||
|
for (const item of input.tasks ?? []) add(item.agent);
|
||||||
|
for (const item of input.chain ?? []) add(item.agent);
|
||||||
|
return [...names];
|
||||||
|
}
|
||||||
|
|
||||||
|
function reminderText(names: string[]): string {
|
||||||
|
const quoted = names.map((name) => `\`${name}\``).join(", ");
|
||||||
|
return (
|
||||||
|
`\n\nPane cleanup: you spawned persistent pane agent(s) ${quoted}. ` +
|
||||||
|
`After you collect the result, call \`stop_subagent\` with the agent name to kill its tmux pane. ` +
|
||||||
|
`Skip only if you plan a follow-up (steer or another subagent call to the same agent); stop it at the end of the workflow. ` +
|
||||||
|
`Stopping preserves the session file, so a later \`subagent\` call resumes the agent's memory.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function (pi: ExtensionAPI) {
|
||||||
|
// Agent names whose panes are still alive this session.
|
||||||
|
const pending = new Set<string>();
|
||||||
|
|
||||||
|
pi.on("tool_call", (event) => {
|
||||||
|
if (event.toolName === "stop_subagent") {
|
||||||
|
const agent = (event.input as { agent?: unknown } | undefined)?.agent;
|
||||||
|
if (typeof agent === "string") pending.delete(agent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pi.on("tool_result", (event, ctx) => {
|
||||||
|
if (event.toolName !== "subagent" || event.isError) return;
|
||||||
|
const input = (event.input as SubagentInput | undefined) ?? {};
|
||||||
|
const paneAgents = agentNamesFrom(input).filter((name) => isPaneAgent(name, ctx.cwd));
|
||||||
|
if (paneAgents.length === 0) return;
|
||||||
|
for (const name of paneAgents) pending.add(name);
|
||||||
|
return {
|
||||||
|
content: [...(event.content ?? []), { type: "text" as const, text: reminderText(paneAgents) }],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
pi.on("before_agent_start", (event) => {
|
||||||
|
if (pending.size === 0) return;
|
||||||
|
const note =
|
||||||
|
"\n\nPane cleanup (pending): " +
|
||||||
|
`[${[...pending].join(", ")}]. ` +
|
||||||
|
"After collecting their results, call stop_subagent for each (unless a follow-up is planned).";
|
||||||
|
return { systemPrompt: event.systemPrompt + note };
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user