@termhub/agent 0.4.0 → 0.4.1
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/dist/cli.js +37 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -611,7 +611,7 @@ function claudeConfigDirs(accountDirs) {
|
|
|
611
611
|
function expandHome(dir, home) {
|
|
612
612
|
return dir.startsWith("~/") ? `${home}/${dir.slice(2)}` : dir;
|
|
613
613
|
}
|
|
614
|
-
var CLAUDE_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "Notification", "Stop", "SessionEnd"];
|
|
614
|
+
var CLAUDE_HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "Notification", "Stop", "SessionEnd"];
|
|
615
615
|
var HOOK_SCRIPT = `#!/bin/sh
|
|
616
616
|
# termhub monitor hook \u2014 installed by termhub; forwards Claude Code / Codex hook events to
|
|
617
617
|
# termhub tagged with the tmux session, so the app knows which tab is waiting for you.
|
|
@@ -626,6 +626,34 @@ SESSION=$(tmux display-message -p -t "$TMUX_PANE" '#{session_name}' 2>/dev/null)
|
|
|
626
626
|
[ -n "$SESSION" ] || exit 0
|
|
627
627
|
if [ "$TOOL" = codex ]; then EVENT="$2"; else EVENT=$(cat 2>/dev/null); fi
|
|
628
628
|
[ -n "$EVENT" ] || EVENT='{}'
|
|
629
|
+
# Tool calls: only the tool's name travels (never its input), and only when it changed since the
|
|
630
|
+
# last one for this session \u2014 twenty edits in a row are one request. The marker is per tmux
|
|
631
|
+
# session, under TMPDIR, with the session name reduced to filename-safe characters.
|
|
632
|
+
MARK="\${TMPDIR:-/tmp}/termhub-hook-$(printf '%s' "$SESSION" | tr -c 'A-Za-z0-9_-' '_')"
|
|
633
|
+
case "$EVENT" in
|
|
634
|
+
*'"hook_event_name":"PreToolUse"'*|*'"hook_event_name": "PreToolUse"'*)
|
|
635
|
+
# The event's own tool name is the FIRST "tool_name" of the payload (Claude Code serialises it
|
|
636
|
+
# before tool_input), so the shortest prefix is cut \u2014 a "tool_name" nested in a tool's input
|
|
637
|
+
# must not win. Only letters, digits, "_", "." and "-" are posted (a bare Claude Code tool name,
|
|
638
|
+
# or an MCP tool name such as mcp__claude-in-chrome__click): anything else (a number, a name with
|
|
639
|
+
# a quote or a backslash) is dropped rather than sent \u2014 those are the only characters the
|
|
640
|
+
# hand-built JSON body below cannot survive as-is.
|
|
641
|
+
REST=\${EVENT#*'"tool_name"'}
|
|
642
|
+
[ "$REST" != "$EVENT" ] || exit 0
|
|
643
|
+
REST=\${REST#*'"'}
|
|
644
|
+
NAME=\${REST%%'"'*}
|
|
645
|
+
case "$NAME" in '' | *[!A-Za-z0-9_.-]*) exit 0 ;; esac
|
|
646
|
+
[ "$(cat "$MARK" 2>/dev/null)" = "$NAME" ] && exit 0
|
|
647
|
+
printf '%s' "$NAME" 2>/dev/null > "$MARK"
|
|
648
|
+
EVENT=$(printf '{"hook_event_name":"PreToolUse","tool_name":"%s"}' "$NAME")
|
|
649
|
+
;;
|
|
650
|
+
# A new turn starts fresh, and so does an answered notification: a permission prompt takes the tab
|
|
651
|
+
# out of working, and the tool the person approves is the same one that set the marker, so without
|
|
652
|
+
# this reset the retry is suppressed and nothing says the tab is working again.
|
|
653
|
+
*'"hook_event_name":"SessionStart"'*|*'"hook_event_name": "SessionStart"'*|*'"hook_event_name":"UserPromptSubmit"'*|*'"hook_event_name": "UserPromptSubmit"'*|*'"hook_event_name":"Notification"'*|*'"hook_event_name": "Notification"'*)
|
|
654
|
+
rm -f "$MARK"
|
|
655
|
+
;;
|
|
656
|
+
esac
|
|
629
657
|
{ printf '{"tool":"%s","session":"%s","event":' "$TOOL" "$SESSION"; printf '%s' "$EVENT"; printf '}'; } |
|
|
630
658
|
curl -s -m 5 -o /dev/null -X POST "$TERMHUB_HOOK_URL" \\
|
|
631
659
|
-H "authorization: Bearer $TERMHUB_HOOK_TOKEN" -H 'content-type: application/json' --data-binary @- >/dev/null 2>&1 &
|
|
@@ -649,7 +677,10 @@ function mergeClaudeSettings(current, scriptPath) {
|
|
|
649
677
|
for (const event of CLAUDE_HOOK_EVENTS) {
|
|
650
678
|
const list3 = Array.isArray(hooks[event]) ? hooks[event] : [];
|
|
651
679
|
const others = list3.filter((e) => !isOurs(e));
|
|
652
|
-
|
|
680
|
+
const entry = { hooks: [{ type: "command", command: `${scriptPath} claude`, timeout: 10 }] };
|
|
681
|
+
if (event === "PreToolUse")
|
|
682
|
+
entry.matcher = "*";
|
|
683
|
+
others.push(entry);
|
|
653
684
|
hooks[event] = others;
|
|
654
685
|
}
|
|
655
686
|
settings.hooks = hooks;
|
|
@@ -925,7 +956,9 @@ async function install(params, home = os3.homedir()) {
|
|
|
925
956
|
async function heal(home = os3.homedir()) {
|
|
926
957
|
const scriptPath = path3.join(home, HOOK_SCRIPT_REL);
|
|
927
958
|
const env = await readOrEmpty2(path3.join(home, HOOK_ENV_REL));
|
|
928
|
-
|
|
959
|
+
const script = await readOrEmpty2(scriptPath);
|
|
960
|
+
if (!env.trim() || !script) return [];
|
|
961
|
+
if (script !== HOOK_SCRIPT) await writeAtomic(scriptPath, HOOK_SCRIPT, 493);
|
|
929
962
|
const healed = [];
|
|
930
963
|
for (const dir of await discoverClaudeDirs(home)) {
|
|
931
964
|
const file = path3.join(expandHome(dir, home), "settings.json");
|
|
@@ -1833,7 +1866,7 @@ async function status3() {
|
|
|
1833
1866
|
}
|
|
1834
1867
|
|
|
1835
1868
|
// src/version.ts
|
|
1836
|
-
var AGENT_VERSION = "0.4.
|
|
1869
|
+
var AGENT_VERSION = "0.4.1";
|
|
1837
1870
|
|
|
1838
1871
|
// src/rpc/update.ts
|
|
1839
1872
|
var PACKAGE = "@termhub/agent";
|
package/package.json
CHANGED