A finding you have to remember to go and look at is a finding you do not see. This closes the loop: `fathom insights` reports findings for other programs, a Claude Code Stop hook runs it when a turn ends, and /insights shows the detail on request. The CLI reports as text, --json, or --jsonl, and --new returns only what has not been reported before. The server owns the reported-findings state, because two sessions can end at the same moment and a read-then-write would have both report the same thing. Two rules follow from running on every turn: - It must be fast. --hook asks a running server for an analysis already computed rather than doing one, and never falls back to analysing directly, which could take seconds. Measured at ~20ms whether or not a server is up. - It must never disrupt the session it observes. Every path exits 0 and prints at most one line. A Stop hook exiting 2 tells Claude Code to keep going rather than stop, so a crash here would hijack the session it was supposed to quietly watch. Surfacing is a higher bar than appearing in the tab, and biased towards silence. A tab is consulted; a notification arrives whether or not you wanted it, and the costs are asymmetric -- a missed finding waits patiently, a wrong nudge trains you to ignore all of them. Medium severity and $0.50 minimum, not already reported unless it has recurred since, and at most one nudge every two hours. A local model, if one is running, can veto a finding as not worth interrupting for. It can never promote one that did not clear the bar, is never consulted about findings below it, and a model failure keeps the finding rather than silencing it. Findings it rejects are recorded, so it is not asked the same question every turn. Also fixes a latent hazard this would have made routine. OpenShard runs tail recovery, which truncates, so a CLI opening a store the server is mid-append to could cut off the chunk being written -- the reader destroying the writer's data. Added a read-only open that skips recovery and opens O_RDONLY, and pointed every read-only command at it, including `fathom query`, which had the same problem already. The nudge's cost figure counted each finding separately, so a set of them could claim more money than the window ever spent: $12.52 against a window costing $7.94. Now counted once per trace. claude/ ships the skill and the hook snippet with an installer. The snippet is printed rather than applied, because a script should not blindly edit a settings file that may already have hooks in it, and a mangled settings.json silently disables everything in it.
47 lines
1.5 KiB
Bash
Executable File
47 lines
1.5 KiB
Bash
Executable File
#!/bin/sh
|
|
# Install the /insights skill and print the Stop-hook snippet.
|
|
#
|
|
# The skill is copied; the hook is only printed. Merging JSON into a settings
|
|
# file that may already contain hooks is not something a script should do
|
|
# blindly, and a mangled settings.json silently disables every setting in it.
|
|
set -e
|
|
|
|
here=$(cd "$(dirname "$0")" && pwd)
|
|
dest="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/skills/insights"
|
|
|
|
mkdir -p "$dest"
|
|
cp "$here/skills/insights/SKILL.md" "$dest/SKILL.md"
|
|
echo "Installed the /insights skill to $dest"
|
|
|
|
fathom=$(command -v fathom || echo "$here/../fathom")
|
|
if [ ! -x "$fathom" ]; then
|
|
echo
|
|
echo "Note: no fathom binary found on PATH or at $fathom."
|
|
echo "Build one with: go build -o fathom ./cmd/fathom"
|
|
fi
|
|
|
|
cat <<'SNIPPET'
|
|
|
|
To be nudged when there is something new, add this to the "Stop" array in
|
|
~/.claude/settings.json. If a "Stop" array is already there, add this object
|
|
alongside what is in it rather than replacing it:
|
|
|
|
{
|
|
"hooks": [
|
|
{
|
|
"type": "command",
|
|
"command": "FATHOM=\"$(command -v fathom || echo \"$HOME/work/fathom/fathom\")\"; [ -x \"$FATHOM\" ] && \"$FATHOM\" insights -hook 2>/dev/null || true",
|
|
"timeout": 5
|
|
}
|
|
]
|
|
}
|
|
|
|
Then check it parsed:
|
|
|
|
jq -e '.hooks.Stop' ~/.claude/settings.json
|
|
|
|
The hook is silent unless there is something new above the bar, says nothing
|
|
more than once every two hours, and prints nothing at all if the server is not
|
|
running. To turn it off later, use /hooks or delete that object.
|
|
SNIPPET
|