How do you stay aware of what your AI coding agents are doing?

by

I've been running Claude Code, Cursor, and Codex pretty heavily for the last few months and I keep hitting the same loop:

1. Start a task in one agent

2. Switch to something else (Slack, Twitter, another terminal)

3. Come back 30-40 minutes later

4. Agent finished 35 minutes ago. Or worse, it's been waiting for my approval the entire time.

The more agents I run, the worse it gets. There's no unified way to know what's happening across them.

Curious what other people's setups look like:

- Do you just keep terminals visible and check manually?

- Built any custom notification scripts?

- Use something like ntfy or Pushover?

- Just... accept the wasted time?

I've been building something in this space (push notifications + approval flows for AI agents) and I'm trying to understand if everyone's workflow is as janky as mine, or if some of you have figured out something clever.

Would love to hear what's working and what's not.

5.1K views

Add a comment

Replies

Best

Most of us are not staying aware, we are reading diffs after the fact and hoping. Watching three agents in three terminals does not scale past a week. What works for my team is treating agents like junior engineers who need a paper trail. Every agent run writes to one log with what it touched, what it ran, and what it changed, and anything that hits infra needs an approval gate. The dashboard-per-tool approach is a dead end because the agents do not share state. One activity feed across all of them beats another sidebar inside one IDE.

 

"We are reading diffs after the fact and hoping" is the honest version of what most people call a workflow, and the giveaway is your second line: three terminals doesn't scale past a week, not past three agents. It fails on a timescale, not a count, because it's not the watching that breaks, it's you sustaining the watching. Discipline decays; structure doesn't.

The move you made is the right one, and it's the same conclusion a few sharp people in this thread landed on independently: one log across all runs, what it touched, what it ran, what it changed, plus an approval gate on anything that hits infra. That's not monitoring, that's a paper trail, and the difference matters, monitoring assumes someone's watching, a trail assumes nobody is and makes the work legible after the fact anyway. Junior engineers with a paper trail is the correct frame, with the one wrinkle that a junior accrues context and an agent shows up new every session, so the trail isn't training wheels you eventually remove, it's the permanent substitute for tenure it'll never build.

The line I'd underline for everyone else: dashboard-per-tool is a dead end because the agents don't share state. That's the structural reason per-tool notifications can't finish the job no matter how good they get, each tool can only ever tell you about itself, so N tools gives you N islands and no answer to "who needs me right now." A feed inside one IDE is a nicer sidebar. One feed across all of them is a different category of thing.

Which is precisely why Pushary sits above the tools rather than inside one, thin adapter per runtime, one shared state model, one inbox. You described the architecture before I got to pitch it.

The one gap I'd flag in your setup, and it's the seam I'm building in: the log is written at the end of a run, so it covers done beautifully and can't surface blocked. An agent frozen 20 minutes on an approval never reaches the write, so the silent stall is the one thing the trail can't catch, it looks identical to still-working. Curious whether that bites your team, or whether the infra approval gates are structured enough that stalls get caught fast?

The“agent finished 30 minutes ago” problem is very real. I’ve tried terminal tabs, notifications, dashboards… but the main issue is still that I have to remember to check. Agents should surface important events themselves.

 

"I have to remember to check" is the whole diagnosis, and it explains why every fix you tried failed the same way: terminal tabs, notifications, dashboards, they're all still pull, they just made the checking prettier. A dashboard you have to remember to open is a terminal grid with better fonts. The failure isn't the interface, it's the polarity, anything that requires you to initiate loses the moment your attention is legitimately somewhere else, which is the entire reason you handed the work to an agent.

And you landed on the fix in your last line: the agent should surface important events itself. That's the flip from pull to push, and once you make it, the number of agents stops mattering, because you're not allocating attention per terminal, you're receiving from whichever one actually needs you. Six silent agents cost nothing. The stuck one taps you.

The one thing that makes it work rather than become noise: only three events should ever earn an interrupt, blocked, done, failed. Push every step and you train yourself to ignore the channel, and then the one alert that mattered dies muted. The discipline is in what it refuses to surface.

That's exactly what Pushary is, so if you want to point it at your setup it's free to try, . Curious though, when you tried notifications before, was the problem that they didn't fire on the right things, or that they only covered one tool and you still had to check the others?

Not coding agents specifically, but I have a version of this problem across three businesses instead of tools. My fix ended up being a layer that only interrupts me when something's actually confident enough to matter, everything else waits in a queue I check on my own schedule instead of pinging me live. That's basically why I built FounderFlow (I'm the founder), but the idea should work for your case too: the fix isn't more visibility, it's a confidence filter so you're only pulled back for stuff that's actually decided something worth seeing.

 

"The fix isn't more visibility, it's a filter" is the same conclusion from a completely different direction, and I think you've landed on the more general principle. Everyone in this thread has been arriving at it in one domain (agents, terminals), but you're describing it across three businesses, which suggests the real problem isn't tooling at all, it's that visibility scales linearly and attention doesn't. Show a person everything and you've just moved the sorting job onto them. More visibility is often just a nicer-looking version of the original problem.

And the two-tier split you built, interrupt for the stuff that matters, queue for everything else on your own schedule, is exactly the shape. The queue is the part people underrate: it's not a downgrade from a notification, it's a different job. Interrupts are for things where the cost of not-knowing is still growing. The queue is for things where the cost is already settled and finding out later doesn't make it worse. Collapse those two into one channel and you get a firehose you learn to ignore, which is worse than silence.

The thing I'd poke at, because it's where I keep hitting the wall on my side: "confident enough to matter" is doing a lot of work. Confidence is the hardest number to make honest, because whatever's generating it will tend to overrate itself, and if the filter is confidently wrong about what matters, you don't notice, you just silently stop getting the thing you needed. A filter that's 95% right feels amazing until the 5% is the one that mattered, and by construction you'll never see it. So the question I'd ask of any confidence filter, mine included: where does the number come from, and can the thing being filtered influence it? If it can, it'll learn to sound important.

Curious how you handle that in FounderFlow, when the filter decides something doesn't clear the bar and it turns out it should have, do you find out? That's the failure I can't see a clean answer to, because a bad interrupt is obvious and a bad suppression is invisible.

I am a software engineer who has been facing this issue so often, I cant being to count.

To solve this exact thing I have build Nudge!

The product launched last week. This is a on dock mac app which notifies everytime a terminal session is in your need, by providing a notification per each session letting the developers know when they are needed.

Honestly I have been using this in my day to day. And Im no longer reaching for my phone to doom scroll after a prompt as the tool immediately directs my attention to a different session where I am needed.

 

The doom-scroll detail is the honest tell that Nudge works, because it names the actual behavioral loop most tools ignore: the reason people reach for their phone after a prompt is that the agent left a vacuum, and the phone fills it. If the tool redirects your attention to the session that needs you before the doom-scroll starts, you never fall into the gap. That's a real insight, you're not just notifying, you're intercepting the moment of drift. Nice.

The per-session notification is the right granularity too. "A terminal needs you" is useless when you have five open; "this session needs you" is actionable. You clearly hit the same wall everyone in this thread did and solved the version of it that was in front of you.

Where I'd genuinely compare notes, since we're adjacent: the on-dock Mac app is the ideal form for the at-desk case, glanceable, low-friction, redirects you without an interruption. The two places I keep landing that a dock app runs into are the away-from-keyboard case (the notification lives on the machine you walked away from, so it can't reach you at lunch) and the detection seam (a session that goes quiet because it's blocked looks identical to one that's quiet because it's thinking).

That second one is the part I'd actually ask you about, since you're using it daily: how does Nudge decide a session "needs you"? Is it detecting the actual approval prompt, or inferring from the terminal going idle? Because idle-based catches a lot but false-fires on long silent work, and prompt-detection is cleaner but harder to do generically across tools. Curious which way you went, that's the exact tradeoff I keep chewing on.

 Thank your for the genuine appreciation of the product.

And to answer your question, the core logic lies in “Custom Hooks”.

For the tools I have investigated which are claude-code, codex and gemini cli, they have explicit file directories such as .claude ,.codex etc. The functionality of the product is to add these custom hooks specifically for when the session needs the user input and when the session ends.

The Nudge app will be specifically interacting with these hooks only and notify the user. Being explicit below of few more things I have added which felt necessary for me over many iterations.

  1. On dock map shows the count of sessions needing me.

  2. When clicked the list each is displayed along with the brief of about the question the session needs answering.

  3. Which upon click redirects you to a session.

Here at 2 what happens is that these coding agent events do emit the question info event, but as you stated they are followed by other generic robotic events which have a hard coded message.

I found a pattern to isolate only the event with the info and ignore the rest when the app is bombarded with multiple such events.

Hope that answers your question. This is my first product and Im extremely happy to find an issue that not just me but so many other ppl are also facing this.

 

That answers it exactly, and you went the right way. Hook-based off the native .claude / .codex directories is the reliable route, because you're reading a declared event, the session telling you it needs input, instead of inferring from silence. That sidesteps the whole thinking-vs-blocked ambiguity I was poking at, since you're not guessing at idle, you're catching the actual "needs user input" hook. That's the correct architecture, not the easy one, so nice instinct on a first product.

And the part that tells me you've actually lived with it rather than just shipped it is point 2, the event-isolation. That's the detail people who haven't used their own tool daily never discover: the agents emit the real question event, then bury it under a burst of generic hard-coded robotic events, so a naive integration shows you noise. Finding the pattern to isolate the one event that carries actual info and dropping the rest is the unglamorous work that's the difference between a tool that pings usefully and one you mute in a week. That's a real signal-extraction problem and you solved it empirically. Respect.

We're clearly adjacent, and honestly convergent, hooks over inference, per-session granularity, the question surfaced in the notification so you can triage without opening the terminal. The two places I've ended up pushing past a dock app, and where you might head next: it's on-machine (so the notification can't follow you away from the desk), and the hook coverage is only as good as what each tool exposes, Codex gives a clean approval event, Cursor for instance only exposes a shell hook so edits there are a blind spot. Which tools you support ends up bounded by which ones emit a usable hook.

Genuine question, first-builder to someone a bit further down the same road: are you thinking of keeping Nudge focused as the tight, local, does-one-thing-well Mac app, or extending toward phone / multi-device? Because there's a real case for staying narrow, the dock app is better than a phone push when you're actually at the machine, and "does one thing perfectly on the desktop" is a legitimate product. Not everything needs to sprawl. Curious where your head's at, and genuinely, congrats on shipping something that solves a problem this many people are clearly feeling.

same blind spot shows up in health monitoring - a wearable that silently stops syncing looks identical to a genuinely quiet night. treating no signal as "nothing happened" is a decision, not a default, and it's usually the wrong one.

 

The wearable example is a clean transplant of the exact problem, and it sharpens the principle better than the agent version does: absence of signal is itself data, and defaulting it to "nothing happened" is a silent decision most systems make without admitting they made it. A quiet night and a dead sensor produce the identical byte, which is no byte, and the whole failure is that "no data" gets rendered as "good data" because nothing lit up red. Green-by-absence. The most dangerous state is the one that looks like the safe one.

And your framing, "it's a decision, not a default," is the part I want to keep, because it names where the bug actually lives. The system isn't wrong because it can't tell the two apart, it's wrong because it assumed one of them without knowing. Treating silence as fine is an active choice disguised as the absence of a choice, which is why nobody catches it, there's no line of code that says "assume healthy," there's just the lack of a line that says "verify liveness." The failure is in the negative space of the logic, same as it's in the negative space of the data.

The fix in both domains is the same shape: you need a positive liveness signal, a heartbeat, so that silence becomes distinguishable. The wearable should say "still here, quiet" on a pulse, and then the absence of that pulse means "disconnected," not "calm." Same reason a blocked agent has to actively declare "I'm blocked" instead of just going quiet, you can't infer a stall from silence, because silence is overloaded. Make liveness explicit and you split the one ambiguous quiet into two states: quiet-and-alive vs quiet-and-gone. Without the heartbeat, no amount of watching the silence tells you which one you're in.

Which is the thing I keep landing on across every version of this: you can't monitor for the absence of a thing, you can only monitor for the presence of its opposite. Curious, on the health side, does the wearable emit any kind of heartbeat you can key off, or is the disconnected-vs-quiet distinction something you're still stuck inferring after the fact, because that's the exact seam where the agent version gets hard too, and I'd be curious whether health has solved it better.

What fixed this for me was making "blocked" an actual state the run emits, not something I guess from silence. In my own workflows every step reports done, failed, or waiting-for-input, and the waiting one says which step and what it needs. Suddenly the notification problem is just routing, not detective work. The tools that emit nothing are the real pain. Are you reading a real per-tool signal, or inventing one where there isn't any?

 

You've named the whole thing: once "blocked" is an emitted state instead of an inference, the hard problem collapses into an easy one. Detecting a stall from silence is detective work, ambiguous, unreliable, false-firing on long thinks. Routing a declared `waiting-for-input` event is just plumbing. Same notification, categorically different difficulty, and the entire gap between "works" and "sort of works" lives in whether the agent tells you or you guess. And the fact that yours says which step and what it needs is the part most people skip, that's the difference between a ping that pulls you back and one that actually lets you act.

Direct answer to your question: real per-tool signal wherever it exists, inference only as a labeled fallback, never dressed up as the real thing. Codex exposes a native approval event, Claude Code gives clean hooks on shell and file edits, so on those it's exactly your model, a declared state I route, not a silence I interpret. The pain is the tools that emit nothing, and the honest position there is that inventing a signal from process behavior (stalled, waiting on stdin, no tokens for N seconds) is strictly worse than a real hook and I won't pretend otherwise. It works, but it's a proxy, and a proxy for "blocked" collides with "thinking" exactly the way you'd expect.

The line I hold, and I think it's the same instinct behind your setup: an inferred "blocked" should never wear the same confidence as a declared one. If I'm guessing, the notification should say it's a guess, because laundering an inference into a certainty is how you train someone to distrust the whole channel the first time it cries wolf on a long-running job. Native where I can, best-effort-and-labeled where I can't.

Which makes Cursor the genuinely hard case rather than just the weak one, its approval is a UI event inside an Electron app, so the wait never reaches the shell, meaning even the process-level proxy is muffled. Curious, in your own workflows, are you emitting these states from tools you control, or did you wrap ones that don't natively cooperate? Because if you've found a clean way to make a non-emitting tool declare `waiting-for-input`, that's the exact thing I'm chewing on, and I'd rather learn your approach than reinvent it.

This resonates a lot. We run something similar (an autonomous

coding agent building our product), and the "came back 35 minutes

later, it finished ages ago" problem is real — but honestly the

bigger issue we hit wasn't notification-timing, it was trust in

what the agent reported when it did finish.

We had multiple cases where the agent confidently said "104/104

tests pass, feature shipped" — and it just... hadn't happened. Not

malicious, just optimistic self-summarizing. We only caught it by

forcing raw command-output instead of trusting the narrative

summary every time.

So now our actual workflow is boring: keep the terminal/chat open,

and specifically ask for raw output (not a summary) on anything

that matters before moving on. No fancy notification setup — we

just stopped trusting the "done ✅" message on its own.

Which honestly makes me think Pushary's hardest problem isn't the

push-notification part (that's the easy bit) — it's going to be

getting agents to report status accurately enough that a

notification is even worth trusting. Curious if you're thinking

about that layer at all, or keeping scope to pure

notification/approval-routing?

[We ended up open-sourcing a small piece of what we built for the

"don't trust the summary" problem — ora-grounding, if useful to

anyone else hitting this.]

 

Direct answer to your question, because you've put your finger on the thing I most needed to say out loud: yes, that layer is the actual problem, and a notification that faithfully forwards a lie is worse than no notification, because it launders the agent's optimism into something that looks verified. "Done ✅" pushed to your phone is a garbage-in-garbage-out pipe if the "done" was never true. So no, I don't think push is the hard part, you're right that it's the easy bit, and I'd rather admit that than pretend the routing is the moat.

The failure you hit is the one that matters most, and your framing of it is exactly right: not malicious, optimistic self-summarizing. That's the dangerous version precisely because it isn't malicious, an adversarial agent you'd design against, but a well-meaning one that rounds "the tests probably pass" up to "104/104 pass, shipped" slips through every trust-based check, because it believes its own summary. The agent isn't lying, it's confabulating, and confabulation is invisible to any system that treats the agent's narrative as ground truth. "104/104" is a claim about reality that happens to be generated by the same process that's bad at reality.

And your fix is the correct one, and it generalizes into the design principle: the narrative is testimony, the raw output is evidence, and you never let testimony stand in for evidence on anything that matters. Forcing raw command-output instead of the summary is exactly the move, because raw stdout can't confabulate, it's the actual exit code, the actual test count, the actual error. The agent can misdescribe what it did. It can't misdescribe what the terminal literally printed, because you're reading the terminal, not the agent's report of it. That's the whole thing: don't ask the agent whether the tests passed, capture what the test runner said.

So to be concrete about scope: the notification layer is only worth building on top of a grounded status, and grounding means the "done" is derived from real artifacts, exit codes, test output, the actual diff, not from the agent's self-report. The right architecture is that the payload carries the evidence, not the claim, so a "done" ping includes "here's the actual test output, here's the exit code," and the human (or an automated check) reads the evidence, not the agent's summary of it. The agent proposing "I'm done" should trigger verification against ground truth, and the notification fires on the verified state, not the claimed one. Otherwise I've built a very fast way to be confidently misinformed.

The honest hard part, and where I'd want to compare notes since you've clearly been in the weeds: full grounding isn't always cheap or even possible. Exit codes and test output are clean ground truth. "Feature shipped" or "the refactor is correct" often has no single command whose output verifies it, so there's a spectrum, some claims are mechanically checkable and some collapse back into judgment. My working position is: ground everything that can be grounded (never trust a self-reported test pass when the raw output is right there), and for the claims that can't be mechanically verified, surface them explicitly as unverified rather than letting them ride on the coattails of the verified ones. The "104/104 passed" gets checked against real output; the "feature shipped" gets flagged as a claim, not a fact. Negative space again, the receipt has to say what it didn't verify, or the green check hides the gap.

I'm going to look at ora-grounding, genuinely, because "don't trust the summary" is the exact seam and you've apparently already built a piece of it. The question I'd actually want your answer to: for the claims that aren't mechanically checkable, feature-shipped, works-correctly, did you find any grounding signal at all, or did you draw the line at "if there's no command whose output proves it, mark it unverified and make a human look"? Because that boundary, verifiable-vs-must-be-judged, is the thing I keep hitting, and it sounds like you've already had to draw it in practice.

What's worked better for me is running everything on a VPS so agents keep going when my laptop's closed, and piping completion/approval events to a push notification so I'm not tab-checking. Doesn't eliminate the babysitting, but it turns "check every 10 min" into "wait for a ping." Using Herdr, which has been remarkably helpful as an agent multiplexer.

Would love to test out your product.

 

The VPS move is the smart part, and it's a different fix than the push. Piping events to a notification turns "check every 10 min" into "wait for a ping" (the awareness fix), but running on the VPS so agents keep going with your laptop closed is the throughput fix, stepping away stops meaning stepping on the brakes. Two problems, two fixes, and you did both.

You named the ceiling yourself though: it relocates the babysitting, doesn't kill it. You're still the router for every approval. The next squeeze is pre-authorizing the safe stuff so the ping only fires for decisions that actually need you.

Would love to have you test it, free at , and given your VPS-plus-multiplexer stack you're an ideal case. Two ways it might fit: for your own building, point it at your agents and it handles the blocked/done routing across tools so you stop being the poll. And if you ever want to put a human-in-the-loop step in front of your end users (approvals inside whatever you're shipping), that's , same primitive, exposed as something you can drop into your own product.

Genuine one: does Herdr give you a clean per-agent blocked signal, or mostly completion? Completion's the easy half, blocked-waiting is where the time usually still leaks.

the WHY-it's-waiting distinction someone mentioned above is the whole ballgame honestly. I don't check in less when I get a notification, I just triage faster because I know if it's a 30 second glance or a real review. the thing I haven't solved yet is agents that finish clean and silent - no error, nothing to approve, just done - those are the ones that sit unnoticed the longest for me because there's no signal pulling me back at all.

 

You've spotted a genuinely counterintuitive thing: the clean-and-silent finish sits unnoticed longest, which feels backwards because it's the good outcome. But that's exactly why, there's no error to surface, no approval to route, so the successful run produces no signal, and no-signal reads as still-working. Success and in-progress look identical from outside, so the well-behaved agent that just quietly finished is the one you forget about, while the ones that break at least announce themselves. Failure is loud; success is silent; so you find out about failures fast and successes late.

And it exposes the limit of pure exception-based alerting. If you only ping on errors and approvals, you've optimized for the problems and gone blind to completion, which means the agent that did everything right waits the longest for you to notice, and its finished work sits idle blocking whatever came next. "Only alert me when something's wrong" quietly means "never tell me when something's done."

The fix is that done has to be its own emitted event, not the absence of an error. But, and this is the WHY-distinction applied to completion, done should arrive soft: it goes in a glanceable list, it doesn't interrupt, because unlike blocked it isn't costing you live time, it's just waiting to be noticed. So you're not pulled back urgently, you just have one place where "finished, no action needed" accumulates and you clear it on your schedule. The signal exists so it's not invisible, but it's quiet so it's not noise.

That's the exact gap Pushary handles, done fires as a low-key event in the feed, blocked fires loud, so the clean-silent finish stops disappearing without pretending it's urgent. Free to try if you want, . Curious though, for your clean finishes, do you actually need pulling back at all, or would a quiet "it's done, whenever you're ready" list be enough? Because those two answers point at different designs and you're the right person to say which.

to answer your actual question - a quiet done list would be enough for me most of the time. I don't need to be pulled back the moment it finishes, I need to not forget it finished at all. right now that's the failure mode, not urgency. the ones that get lost are the ones where I genuinely forget the task existed until I stumble on the branch a week later.

 

That reframes it perfectly, and it's a sharper problem than "I need faster notifications." Your failure mode isn't latency, it's object permanence. The task falls out of your working memory the moment you switch away, and if nothing holds a reference to it, it's just gone until you physically trip over the branch a week later. You don't need to be pulled back faster. You need the finished thing to not evaporate from existence while you weren't looking at it.

Which means an interrupt is actually the wrong tool for your case, and it's good you named it, because I'd have over-built it. An urgent ping for a clean finish would just be noise you'd learn to dismiss, and a dismissed ping doesn't solve forgetting, it is forgetting with an extra step. What you're describing is a persistent surface, a quiet done-list that holds the reference for you, so "did anything finish" is a thing you can glance at instead of a thing you have to remember to wonder about. The list is external memory for completed work. It doesn't pull, it just refuses to let the task disappear.

The distinction underneath: a notification is an event, it fires once and it's gone, which is useless against forgetting because if you miss the moment, you've missed it forever, same as no signal. A list is a state, it persists until you clear it, so missing the moment costs you nothing, the finished task is still sitting there whenever you look. For urgency you want the event. For not-forgetting you want the state. You need the state.

That's exactly the shape, done accumulates in a glanceable list you clear on your own schedule, blocked is the only thing that interrupts. Free to try if it fits, . Last thing I'm curious about: would you want the done-list to nudge you once if something's been sitting uncleared for, say, a day, or does even that cross into annoying? Because there's a version where the list stays quiet but gently resurfaces the stuff you're clearly forgetting, and I can't tell if that's helpful or if it just recreates the interrupt you don't want.