Agentic CodingGuide

Agent Workflows

The plan→execute→review→test loop, skills, and hooks.

Repo & folder organization

How a repo is organized directly affects how well agents navigate and modify it. A well-structured repo is a better feedback signal than documentation.

Layer folders vs feature folders

Agents navigate feature folders far more reliably — everything a payments change touches lives in one place.

Layer folders (agent-hostile)

payments.ts
orders.ts
users.ts
payments.ts
orders.ts
users.ts
payment.ts
order.ts
user.ts
payments.test.ts
orders.test.ts

Feature folders (agent-friendly)

controller.ts
service.ts
model.ts
payments.test.ts
index.ts

Agent-ready repo root

AGENTS.md
CLAUDE.md
README.md
settings.json

The coding agent loop

The core agentic loop: Plan → Execute → Review → Test → Automate, with feedback flowing backward at every stage.

        ┌──────────────────────────────────────────┐
        │                                          │
        ▼                                          │
  ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
  │   PLAN   │──▶│ EXECUTE  │──▶│  REVIEW  │──▶│   TEST   │──▶│ AUTOMATE │
  └──────────┘   └──────────┘   └──────────┘   └──────────┘   └──────────┘
        ▲              ▲              │              │
        │              │              │              │
        │              └──────────────┘              │
        │                   (fix)                    │
        │                                            │
        └────────────────────────────────────────────┘
                      (rethink scope)

The forward arrows are the happy path. The backward arrows are where most of the value comes from: Review kicks bad changes back to Execute, and Test failures can bounce all the way back to Plan when they reveal the scope was wrong.

  • Plan — define the task clearly, use /grill-me to surface assumptions, agree on "done" before starting
  • Execute — one coherent change at a time; break large tasks down
  • Review — linters and types as first pass; human review for what machines can't catch
  • Test — write tests alongside, not after; TDD is cheap with agents
  • Automate — hook into CI so the full loop runs on every PR

Skills

Skills are reusable agent capabilities — packaged prompts, tools, or workflows invoked by name.

  • /grill-me — adversarial assumption flushing
  • /lint-rule — generate a custom ESLint rule from a description
  • /review — structured code review with specific focus areas
  • /test — generate behavior-based tests for a given function or module
  • /explain — plain-language explanation of a code path
  • /refactor — guided refactoring with safety checks

Focused skill reviews

Every codebase has attributes worth enforcing that no off-the-shelf linter knows about — your React patterns, your API conventions, your query discipline. Run narrow, focused skill-based reviews in parallel in CI, one per concern. Each skill knows one thing about your codebase and checks it thoroughly.

Good candidates — anything too semantic for a linter, that you'd normally leave the same code review comment about repeatedly:

  • React best practices — re-renders, missing keys, stale effect dependencies, improper memoization
  • API data flow — type alignment, auth on protected routes, missing rate limits, inconsistent error shapes
  • DB queries — N+1 patterns, missing indexes, unsafe raw SQL, migration rollback safety
  • Security patterns — ownership checks, input sanitization, session/token handling
  • Observability gaps — new endpoints without logging/metrics, external calls without timeout/retry

Use openai/codex-action or anthropics/claude-code-action to run a focused review agent per PR. Each action takes a prompt file that defines what the reviewer cares about.

.github/codex/prompts/react-review.md
Review this pull request strictly for React best practices.

Load the rules in `.agents/skills/react-best-practices/README.md` and
apply them to every changed `.tsx` file.

For each finding, output:
- File and line
- Severity (critical | warn | note)
- One-sentence explanation of what's wrong
- Suggested fix

Hooks (Claude Code)

Hooks run shell commands at specific points in the agent lifecycle — the primary extension point for injecting feedback signals, enforcing guardrails, and automating quality steps.

Configured in .claude/settings.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [{ "type": "command", "command": "npm run lint:fix $CLAUDE_TOOL_INPUT_PATH" }]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "~/.claude/hooks/check-dangerous-commands.sh" }]
      }
    ]
  }
}

Hook types:

  • PreToolUse — runs before a tool executes; exit 2 blocks the action and feeds stderr back to the model
  • PostToolUse — runs after a tool completes; ideal for formatting, linting, logging
  • Notification — triggered when Claude sends a notification
  • Stop — triggered when Claude finishes a task

Practical uses — each is a feedback signal the agent receives immediately:

  • Auto-format on write — Prettier on every edit
  • Lint on edit — ESLint --fix after every file change
  • Type check on changetsc --noEmit after TS edits
  • Test on change — run the relevant test file; keep the red-green loop tight
  • Dangerous command check — scan Bash commands for risky patterns before they run

Example: enforce a PR-based workflow

A PreToolUse hook that blocks direct commits and pushes to main, forcing agents to work in feature branches with PRs:

.claude/hooks/block-main-writes.sh
#!/usr/bin/env bash
# Exit 2 tells Claude Code to block the tool call and feed stderr back to the model.

set -euo pipefail

COMMAND=$(jq -r '.tool_input.command // ""')
[[ "$COMMAND" == *"git"* ]] || exit 0

CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
PROTECTED='^(main|master)$'

if [[ "$COMMAND" == *"git commit"* ]] && [[ "$CURRENT_BRANCH" =~ $PROTECTED ]]; then
  cat >&2 <<EOF
Refusing to commit directly to '$CURRENT_BRANCH'.

Create a feature branch and open a PR instead:
  git checkout -b feat/<short-name>
  git commit ...
  git push -u origin HEAD
  gh pr create --fill
EOF
  exit 2
fi

if [[ "$COMMAND" == *"git push"* ]] && \
   [[ "$COMMAND" =~ (^|[[:space:]:])(main|master)([[:space:]]|$) ]]; then
  echo "Refusing to push directly to main/master. Open a PR instead: gh pr create --fill" >&2
  exit 2
fi

if [[ "$COMMAND" == *"git push"* ]] && [[ "$CURRENT_BRANCH" =~ $PROTECTED ]]; then
  echo "Refusing to push from '$CURRENT_BRANCH'. Switch to a feature branch and open a PR." >&2
  exit 2
fi

exit 0
.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "bash .claude/hooks/block-main-writes.sh" }
        ]
      }
    ]
  }
}

The refusal message becomes the agent's next-step instruction — because stderr is fed back on exit 2, the agent reads "create a feature branch and open a PR" and actually does it.

  • settings.json (checked in) — shared hooks for the whole team
  • settings.local.json (gitignored) — personal hooks, local logging, preferences

Hooks are where policy lives. Prompts describe what to do; hooks enforce what's allowed — and they run whether you're in the loop or not.

On this page