<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/DoQr1tzdO" -->

---
title: How Claude Code mods change the agent loop | daily.dev
description: Claude Code mods are JavaScript or TypeScript handlers that can intercept, rewrite, or block events inside a coding session, unlike simpler settings hooks. The...
canonical: https://daily.dev/posts/how-claude-code-mods-change-the-agent-loop-doqr1tzdo
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: How Claude Code mods change the agent loop | daily.dev
og:description: Claude Code mods are JavaScript or TypeScript handlers that can intercept, rewrite, or block events inside a coding session, unlike simpler settings hooks. The...
og:url: https://daily.dev/posts/how-claude-code-mods-change-the-agent-loop-doqr1tzdo
og:image: https://api.daily.dev/og/posts/DoQr1tzdO.png
og:image:alt: How Claude Code mods change the agent loop
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How Claude Code mods change the agent loop

**[Flavio Copes](https://daily.dev/sources/flaviocopes)** · 7 min read · 5 upvotes · 1 comments

## Summary

Claude Code mods are JavaScript or TypeScript handlers that can intercept, rewrite, or block events inside a coding session, unlike simpler settings hooks. The post builds a small mod that pauses every Bash tool call and asks the user to approve or cancel it before continuing. The handler chain uses next(e) to proceed and returns a deny object to block, with a .catch handler required to fail closed if the hook errors before calling next. A companion test file with eight Node tests checks the deny, approval, and failure-after-next branches without executing real commands. The author notes mods aren't a sandbox: they have access to files, processes, network requests, and secrets, and a text preview of a shell command doesn't fully capture its effects.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://flaviocopes.com/claude-code-mods>

## Questions this post answers

### How do I make Claude Code ask for approval before running a Bash command?

Register a mod handler on the 'tool.call' event matched to the Bash tool, call an async UI question like $.ui.ask with options such as 'Run once' and 'Cancel', then call next(e) only if the answer equals the approval choice; otherwise return a deny object with a reason instead of calling next.

_daily.dev surfaces guides like this for developers wiring up custom Claude Code agent guardrails._

### What is the difference between a Claude Code mod and a settings hook?

A settings hook is configured around events like tool use and invokes an external command or hook handler, passing input and reading a result, while a mod registers JavaScript or TypeScript functions directly inside Claude Code whose handlers form a chain that can call next(e) to continue, pass a changed event to rewrite it, or return a result directly.

_Developers choosing between Claude Code extension mechanisms can track these distinctions on daily.dev._

### What happens if a Claude Code mod hook throws an error before calling next?

By default Claude Code skips a hook that fails before calling next and lets the remaining chain proceed, which can unintentionally allow a command through. A registered .catch handler can check next.called and, if next hasn't run, return a deny result instead, ensuring the guard fails closed rather than silently permitting the action.

_daily.dev helps developers building fail-closed agent guardrails stay on top of Claude Code's hook behavior._

## Community discussion

Top comments from developers on daily.dev.

**@kartiknvj** · 0 upvotes

> Your preview section says a command's meaning depends on its working directory, but the ask prompt only shows e.command. Does the [tool.call](http://tool.call) event carry the cwd? I'd print it on the line above the command, because rm -rf build means something very different in two folders.

## Similar posts on daily.dev

- [Claude Code can now be modded like a video game, and I couldn't stop tinkering](https://daily.dev/posts/claude-code-can-now-be-modded-like-a-video-game-and-i-couldn-t-stop-tinkering-dzon9seci) · XDA Developers · 4 upvotes · 0 comments
- [Claude Code power user customization: How to configure hooks](https://daily.dev/posts/claude-code-power-user-customization-how-to-configure-hooks-i0akqx7ke) · Claude · 1 upvotes · 0 comments

---

Tags: [#javascript](https://daily.dev/tags/javascript), [#ai-agents](https://daily.dev/tags/ai-agents), [#testing](https://daily.dev/tags/testing), [#claude-code](https://daily.dev/tags/claude-code)

[View this post on daily.dev](https://daily.dev/posts/how-claude-code-mods-change-the-agent-loop-doqr1tzdo)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"How Claude Code mods change the agent loop","url":"https://daily.dev/posts/how-claude-code-mods-change-the-agent-loop-doqr1tzdo","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/how-claude-code-mods-change-the-agent-loop-doqr1tzdo"},"datePublished":"2026-11-01T14:27:36.659Z","dateModified":"2026-11-01T14:28:06.203Z","description":"Claude Code mods are JavaScript or TypeScript handlers that can intercept, rewrite, or block events inside a coding session, unlike simpler settings hooks. The...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/f5769c7eb398b0c2c485a7b875c72551?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/f5769c7eb398b0c2c485a7b875c72551?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Flavio Copes","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Flavio Copes","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/1f2dbd275797411bbd37bfdaaf0951a9","url":"https://daily.dev/sources/flaviocopes"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/how-claude-code-mods-change-the-agent-loop-doqr1tzdo","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":5},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"javascript,ai-agents,testing,claude-code","timeRequired":"PT7M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Flavio Copes","item":"https://daily.dev/sources/flaviocopes"},{"@type":"ListItem","position":3,"name":"How Claude Code mods change the agent loop"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/how-claude-code-mods-change-the-agent-loop-doqr1tzdo","comment":[{"@type":"Comment","text":"Your preview section says a command’s meaning depends on its working directory, but the ask prompt only shows e.command. Does the tool.call event carry the cwd? I’d print it on the line above the command, because rm -rf build means something very different in two folders.","datePublished":"2026-11-03T13:43:18.592Z","url":"https://daily.dev/posts/DoQr1tzdO#c-pcDxAu9Eu","author":{"@type":"Person","name":"kartik-nvjk","url":"https://daily.dev/kartiknvj","image":"https://media.daily.dev/image/upload/s--3gGgsVCw--/f_auto/v1781456774/avatars/avatar_TvTVeiMdkRCqWUDullFmy?_a=BAMAMiWQ0"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/how-claude-code-mods-change-the-agent-loop-doqr1tzdo#faq","mainEntity":[{"@type":"Question","name":"How do I make Claude Code ask for approval before running a Bash command?","acceptedAnswer":{"@type":"Answer","text":"Register a mod handler on the 'tool.call' event matched to the Bash tool, call an async UI question like $.ui.ask with options such as 'Run once' and 'Cancel', then call next(e) only if the answer equals the approval choice; otherwise return a deny object with a reason instead of calling next. daily.dev surfaces guides like this for developers wiring up custom Claude Code agent guardrails."}},{"@type":"Question","name":"What is the difference between a Claude Code mod and a settings hook?","acceptedAnswer":{"@type":"Answer","text":"A settings hook is configured around events like tool use and invokes an external command or hook handler, passing input and reading a result, while a mod registers JavaScript or TypeScript functions directly inside Claude Code whose handlers form a chain that can call next(e) to continue, pass a changed event to rewrite it, or return a result directly. Developers choosing between Claude Code extension mechanisms can track these distinctions on daily.dev."}},{"@type":"Question","name":"What happens if a Claude Code mod hook throws an error before calling next?","acceptedAnswer":{"@type":"Answer","text":"By default Claude Code skips a hook that fails before calling next and lets the remaining chain proceed, which can unintentionally allow a command through. A registered .catch handler can check next.called and, if next hasn't run, return a deny result instead, ensuring the guard fails closed rather than silently permitting the action. daily.dev helps developers building fail-closed agent guardrails stay on top of Claude Code's hook behavior."}}]}
```

