At Lingo.dev, we’re building an AI-powered localization platform. As you can probably imagine, we’re a big fan of using AI to help speed up our workflows.
One of our favorite tools is Claude Code, the AI coding assistant from Anthropic, which we’re using to explore ideas, ship features, and create documentation.
But while Claude Code is powerful out of the box, its real potential emerges when you customize it to fit your workflow. In this article, I’m going to walk through a handful of tips and tricks that help you move beyond simply entering prompts into a text box.
Something I find annoying is that Claude Code regularly tries to start my development server when it’s already running. This is almost never necessary and, when it is, I’d rather be the one to make the decision.
You can solve these kinds of paper cuts by using hooks to:
For example, here’s how to solve the problem of Claude Code starting the development server:
input=$(cat) command=$(echo "$input" | jq -r '.tool_input.command') if [[ "$command" =~ ^pnpm[[:space:]]+(run[[:space:]]+)?dev($|[[:space:]]) ]]; then echo '{"decision": "block", "reason": "Do not attempt to start, stop, or restart the development server. The development server is already running."}' exit 0 fi exit 0
chmod +x ~/.claude/hooks/block-pnpm-dev.sh
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "~/.claude/hooks/block-pnpm-dev.sh" } ] } ] } }
In Claude Code, slash commands are reusable prompts. They have all sorts of interesting use cases, but one of the most fun use cases is for improving Claude Code itself.
For example, I didn’t create the above hook myself. I instead used the following slash command that Claude created for me:
--- description: Create and configure a hook for Claude Code --- ## Role You are an expert at creating Claude Code hooks. Based on the user's request, generate a complete hook configuration and implementation using Bash scripts. ## Initial Analysis Before creating new hooks, always: 1. Check existing hook configuration with `/hooks` command 2. Look for existing hook scripts in `~/.claude/hooks/` 3. Review current settings files: - `~/.claude/settings.json` (user settings) - `.claude/settings.json` (project settings) - `.claude/settings.local.json` (local settings) 4. Identify any existing hooks that might conflict or could be extended 5. Note the naming conventions and patterns already in use ### Commands to examine existing hooks: ```bash # List existing hook scripts ls -la "~/.claude/hooks/" # Check settings files cat ~/.claude/settings.json | jq '.hooks' cat .claude/settings.json | jq '.hooks' cat .claude/settings.local.json | jq '.hooks' 2>/dev/null ``` ## Input The user will describe what they want their hook to do. This might include: - When it should trigger (before/after certain operations) - What it should check or validate - What actions it should take - Any specific conditions or rules ## Output Format Always provide: 1. **Existing Hooks Analysis** (if any found) - Current hook configurations - Existing hook scripts in the project - Potential conflicts or integration points 2. **Hook Configuration** (JSON for settings.json) - Must use dedicated .sh files 3. **Bash Script** (complete .sh file implementation) 4. **Installation Instructions** 5. **Usage Notes** ## Important Requirements - **Always use Bash scripts** (not Python or inline commands) - **All hooks must reference dedicated .sh files** in the command field - **Use `~/.claude/hooks/` as the standard location** - **Scripts must be executable** (`chmod +x`) ## Documentation Reference For complete hook details, events, and specifications, see: https://docs.anthropic.com/en/docs/claude-code/hooks-reference ## Example Hook Patterns ### Validation Hook (using exit codes) ```bash #!/usr/bin/env bash # Read JSON input from stdin input=$(cat) tool_name=$(echo "$input" | jq -r '.tool_name') command=$(echo "$input" | jq -r '.tool_input.command // ""') if [[ "$tool_name" == "Bash" ]]; then # Validation logic here if [[ "$command" =~ some_pattern ]]; then echo "Error: Invalid command pattern" >&2 exit 2 # Blocks operation fi fi exit 0 ``` ### JSON Output Hook ```bash #!/usr/bin/env bash # Generate JSON output for advanced control cat <<EOF { "decision": "approve", "reason": "Auto-approved by policy", "suppressOutput": true } EOF exit 0 ``` ### Context Addition Hook ```bash #!/usr/bin/env bash # Add context for UserPromptSubmit echo "Current time: $(date)" echo "Git branch: $(git branch --show-current 2>/dev/null || echo 'not in git repo')" exit 0 ``` ## Standard Configuration Template ```json { "hooks": { "EventName": [ { "matcher": "ToolPattern", "hooks": [ { "type": "command", "command": "~/.claude/hooks/your-hook-name.sh" } ] } ] } } ``` ## Generation Guidelines 1. First examine existing hooks and configuration 2. Maintain consistency with existing naming conventions and patterns 3. Create focused, single-purpose bash scripts 4. Use `jq` for JSON parsing (it's available in Claude Code environment) 5. Always quote variables: `"$VAR"` 6. Include proper error handling 7. Add helpful comments in scripts 8. Use descriptive hook file names (e.g., `validate-bash-commands.sh`, `protect-sensitive-files.sh`) 9. Test scripts can be run standalone for debugging 10. Consider whether to extend existing hooks vs. creating new ones Generate a complete, working solution based on the user's requirements. Reference the documentation URL for any specific details about hook behavior or available fields. ## User's request $ARGUMENTS
Now, I only have to explain what the hook should do, and Claude Code will read the documentation, create the script, and update the settings. You can use this same approach for hooks, agents, and whatever else Claude Code will support in the future.
Slash commands have an interesting feature that allows Bash commands to be executed before the slash command is run, and for that output to be included within the context of the command.
For example, in one of my slash commands, I have the following statement:
!`list_open_ports`
The !
operator tells Claude to execute a list_open_ports
function that I’ve defined in my .zshrc
file. This function returns all of the active ports on my machine, including human-readable labels for each process. When the slash command runs, the above statement is replaced with the output of the function.
The end result is that, when I run the slash command, Claude is immediately aware of active ports, which means it doesn’t have to waste time figuring out the port of any development servers I have running.
In the official documentation, Anthropic demonstrates something similar for including useful context from Git:
--- allowed-tools: Bash(git add:*), Bash(git status:*), Bash(git commit:*) description: Create a git commit --- ## Context - Current git status: !`git status` - Current git diff (staged and unstaged changes): !`git diff HEAD` - Current branch: !`git branch --show-current` - Recent commits: !`git log --oneline -10` ## Your task Based on the above changes, create a single git commit.
But this isn’t just useful for creating Git commits. The changes on the current branch are useful context for most commands.
Git worktrees are a feature of Git that allow multiple branches to be checked out at the same time, with each branch getting its own directory.
The useful thing about worktrees is that they make it trivial for multiple AI agents to be working on the same codebase without conflicting with each other. (They’re also just nice to work with in general. I much prefer being able to open different branches by opening folders.)
To create a worktree (and the associated branch), run the following command:
git worktree add -b my-branch-name ../my-dir-name
(Be sure to create the directory for the worktree outside of the repo itself.)
You can then fire up an instance of Claude Code within that directory while continuing to work on your current branch — or even create multiple worktrees for peak vibe coding.
One of my favorite things about working with AI is being able to quickly prototype ideas. Certain things that may have taken too much effort before can now be smashed out with minimal thought or effort.
To take this even further, I’ve set up a Bash function that:
The goal is to make it trivial to experiment with ideas and delegate small tasks to agents before returning to whatever I’m primarily focusing on.
Claude Code does a lot of things right, but there are some interesting contenders in the same space — some of which sit on top of Claude Code, so you’re not having to pay for an entirely separate tool.
In particular, I’m quite bullish on:
Small adjustments can make Claude Code noticeably smoother and more capable. The ideas here aren’t exhaustive, but they show how a bit of customization can remove friction and open up new ways to work. From there, it’s just a matter of refining what works best for you.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowReact Router v7 is now more than routing. This guide explains its three modes—declarative, data, and framework and how to choose the right one for your app.
CSS @function brings reusable logic to native CSS. Learn how to replace Sass and JavaScript utilities with native functions for typography, color, and fluid design.
Discover a handy pattern for routing LLM calls in an “environment-aware” manner, using AI SDK’s middleware.
React Server Components aim to simplify data fetching and boost performance, but quirks in caching and loading often turn simple apps into debugging puzzles.