agentreflex
Concepts

Reflexes

A reflex is a module that reacts to what an agent is about to do and returns a decision.

A reflex is the unit you write, install, and share. It's a module whose default export has a name and one or more lifecycle handlers.

export default {
  name: "no-force-push",
  onToolCall(ctx) {
    if (ctx.tool === "Bash" && /git\s+push\b.*--force\b/.test(ctx.command ?? ""))
      return { action: "deny", reason: "no force-push — open a PR instead" };
    return { action: "pass" };
  },
};

Where reflexes live

Reflexes sit in .reflex/ and are listed in .reflex/config.json:

{
  "reflexes": ["./no-force-push.mjs", "./no-secrets.mjs"]
}

arx init, arx add, and arx new manage this for you. Edit a reflex file and it takes effect on the next tool call — the dispatcher loads them fresh each time.

Ordering

Reflexes run in listed order. The first non-pass decision wins, so a deny short-circuits the rest. Stack them, reorder them, drop what you don't need.

Typed authoring

Plain .mjs runs anywhere. For types and autocomplete, author in TypeScript with defineReflex from @agentreflex/core and compile to .mjs:

import { defineReflex, deny, pass } from "@agentreflex/core";

export default defineReflex({
  name: "no-force-push",
  onToolCall(ctx) {
    if (ctx.tool === "Bash" && ctx.command?.includes("--force"))
      return deny("no force-push — open a PR instead");
    return pass();
  },
});

Where they come from

  • Local — files you write in .reflex/.
  • Shared — added from the commons, a URL, or a GitHub path with arx add.

See Distribution.

On this page