惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
Lohrmann on Cybersecurity
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recorded Future
Recorded Future
S
Schneier on Security
I
Intezer
Latest news
Latest news
N
News and Events Feed by Topic
Scott Helme
Scott Helme
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
量子位
博客园 - 【当耐特】
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
博客园 - 叶小钗
酷 壳 – CoolShell
酷 壳 – CoolShell
NISL@THU
NISL@THU
The Cloudflare Blog
李成银的技术随笔
T
ThreatConnect
L
LINUX DO - 最新话题
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
有赞技术团队
有赞技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Jina AI
Jina AI
T
Tor Project blog
The Hacker News
The Hacker News
人人都是产品经理
人人都是产品经理
小众软件
小众软件
S
Security Archives - TechRepublic
美团技术团队
博客园 - Franky
Security Latest
Security Latest
J
Java Code Geeks
P
Proofpoint News Feed
V
V2EX
The GitHub Blog
The GitHub Blog
WordPress大学
WordPress大学
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Help Net Security
PCI Perspectives
PCI Perspectives
Cyberwarzone
Cyberwarzone
Hugging Face - Blog
Hugging Face - Blog
N
Netflix TechBlog - Medium
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
SecWiki News
SecWiki News
腾讯CDC
爱范儿
爱范儿
D
Docker

DEV Community

Mobile Game Optimization: A Unity Developer's Checklist Git: Best Practices for Beginners Three days I lost chasing a ghost that was already dead on disk Why Too Many Parts Hurt ClickHouse Performance Guardrails for Agent Output: Pluggable Validation Before and After LLM Calls Gemma Forge: Local AI Without the Setup Wall From Half‑dead Prototype to Local‑Only AI Medical Assistant: Rewiring MedClinic with GitHub Copilot Runninig a forkbomb in Jenkins What’s Actually Happening When You Use Git Preventing Recursive Tool Loops in LangChain Agents Building a Rock-Paper-Scissors CLI with TypeScript — Union Types, Conditionals, and Jest Your AI Coding Agent Wastes 80% of Its Context. Fixed That with Graph Theory. Why Flutter Has Become the Go-To Framework for Fintech App Development We built a scripting language just for AI agents. Here's why. Stop building AI inboxes. Build decision layers instead. Meme Monday Why I Built @editora/ui-react? Are AI tools the next level of abstraction in software development? Identity on Solana: Your Wallet Is Your Account One API Call Changed Everything The Internet Career Nobody Talks About Enough: What Is DevRel? Solar Panel Wiring Diagram: Series vs Parallel Hello everyone! Glad to join the dev.to community I Built an AI Agent That Tailors My Resume - Here's How Agents Actually Work I Built a WhatsApp OTP + AI Chatbot Platform for African Businesses MTP Explained — And Why It Matters for Android on Mac Most Beginners Learn Full-Stack Development Backwards GitHub Glow-Up: Open Source, READMEs, Badges, Streaks, Git and gh CLI System Design Cheat Sheet: Concepts Every Developer Should Know Are Junior Developer Roles Actually Dying? A Fresher's Honest Take Using DigitalOcean Droplets as Ephemeral Sandboxes for AI Agents I built a VSCode extension that visualises your code navigation as a call tree — made for legacy codebase pain Vite predev/prebuild: chaining scripts without losing your mind A website to save you from messy browser tabs Dear Web2 Developer... Solana is here calling Postgres JSONB indexes: GIN vs BTREE on the same column The $5 AI That Remembers Everything What are your goals for the week? #180 Zettelkasten for Developers: A Practical Method That Works OpenClaw vs Hermes Agent: Stars, Downloads & Usage 2026 `act` vs. `waitFor` Global Teams Don’t Struggle With Time Zones. They Struggle With Context Python as a JavaScript Dev $5.4 Billion in Damage. 8.5 Million Machines Down. Three YAML Controls Would Have Prevented It. Here's the Structural Analysis. 🚫 Stop Using PN532 V1 for Your NFC Projects (Real Debugging Experience) Probabilistic Graph Neural Inference for smart agriculture microgrid orchestration for extreme data sparsity scenarios Inference Is Becoming the New Steady-State Cost Center Why AI-Generated Code Is Always Good Enough — And Never Great I built a dark admin dashboard template in HTML — no React, no npm, just pure HTML What is the Difference Between Lattice-Based and Hash-Based Signatures? Next.js App Router caching: revalidate, dynamic, and no-store without the folklore Next.js App Router caching: revalidate, dynamic y no-store sin folklore I built Stashly — a full-stack content manager with a rich text editor published: false tags: react, node, mongodb, typescript Why I Started Building React Projects Instead of Just Watching Tutorials ? Every Tool Eventually Becomes Tuesday Nobody Warns You That Real Software Engineering Feels Chaotic Tích hợp VNPay, Stripe trong Odoo 19 BeautifulSoup and Requests for Web Scraping With Python: When Simple Still Works I Was Stuck Debugging React — Then Developer Tools Changed It Buck Converter Ripple: Sizing the Inductor and Capacitor With Confidence AWS Just Made Its MCP Server Generally Available. Here's What It Actually Gives AI Agents. RAMPART Tests Your AI Agents in Dev. What Catches Malicious Tool Calls in Production? Vibe Team Software Engineering: What a Real AI Human Dev Team Workflow Actually Looks Like An npm Package for AI Agent Orchestration Just Shipped With Its Front Door Unlocked. Here's What the CVE Actually Reveals. Microsoft Foundry Just Added CI/CD for AI Agents. Here's What That Actually Changes. The Best Career Insurance Is a Tech Event You Don't Want to Attend Your GitHub Profile Already Tells Recruiters More Than Your Resume. Most Devs Just Don't Surface It. How to Add Execution Budgets to OpenAI Agents SDK Binary Tree Interview Problems: 6 Traversal Patterns, 15 Problems We trained a personal voice DoRA on Qwen3-8B for $1.50 — beat stock model 100% in blind A/B Stop Leaking API Keys: Why I Built a Local-First Vault for Developers 🔐 RAG Explained: How Retrieval-Augmented Generation Actually Works I Built a Fast Async JioSaavn API Wrapper in Python 🎧 chown & chgrp Deploying Your First App on Kubernetes: A Beginner's Guide (Minikube & Kind) Logs in code It's called a PR "review" for a reason DePIN GPU Market: The Failed Job Receipt Developers Should Demand Why Your AI Agent Monitoring is Wrong (And How to Fix It) Lock Down Your Cloud Shares: A Beginner’s Guide to Azure Files Security. Building a Multi-Channel Content Syndication Pipeline with EmDash Plugins Turn Your Phone Into Voice Input for Any React Text Field Which package is bloating your Docker image? Putting Claude Code Under Version Control: Configs Since July, Memory Since April What I Thought DevRel Was vs. What It Actually Is (A Mentee's Honest Take) What I Thought DevRel Was vs. What It Actually Is (A Mentee's Honest Take) 400 Million Tokens Burned Overnight Reviving My Linux Mastery Game from a Merge Conflict — A Finish-Up-A-Thon Comeback Don’t let AI break your collective thinking: a practical guide for engineering teams First Gemma 4 ExecuTorch Deployment on Raspberry Pi 5 — and Why It's 7.7 Slower Than llama.cpp Per-Turn Evaluation: Dynamic Governance for AI Agents The AI Triforce of seed4j: Power, Wisdom, and Courage for Your Dev Agent Your AI agent reports 80% task completion. It fabricated it. Pourquoi les overlays d'accessibilité ne tiennent pas leurs promesses (et ce que la FTC vient d'acter) AI May Break Product-Market Fit in Enterprise Software I’m Building Around the Gap Between AI Output and Repo Truth How to Build a Stripe Customer Portal in Next.js SaaS On-Demand Pricing Feels Safe - Until You See the Bill Building an Internal Developer Portal with Backstage A Production Deployment Guide After the Last Song
I wrapped Claude Code in a zsh function. Here's every decision I almost got wrong.
Igor Kramar · 2026-05-25 · via DEV Community

Claude Code's --help lists 50+ flags. After two weeks of using it daily, I built a zsh wrapper called cco that bakes in the flags I actually want. The wrapper itself is 60 lines. The interesting part is the decisions behind those 60 lines — most of them I had to backtrack on at least once.

This is the decision log. If you're using Claude Code seriously, some of these will save you the same backtracks.

Decision 1: Function, not alias, not shell script

The dumb instinct is alias cc="claude --permission-mode acceptEdits --append-system-prompt ...". It works until you want subcommands. cco plan, cco safe, cco review — aliases can't branch on arguments.

Standalone shell script in ~/.local/bin/cc was the next thought. It works for most cases, but spawns a subshell. That's fine for stateless commands. It's not fine when the command wraps an interactive process that wants the parent terminal's tty for tmux attachment and prompt rendering. Worked in testing, behaved weird in edge cases.

A zsh function runs in the current shell. Inherits the tty cleanly. Can dispatch on subcommands. Can be tab-completed via compdef. That's what I went with.

Cost: lives in your .zshrc (or a sourced module file). Not portable to bash users without rewriting. I'm fine with that — I'm not shipping this to other people.

Decision 2: cc vs cco

I picked cc first. Two letters, mnemonic for "Claude Code". I almost committed it.

Then I checked my aliases file. cl was already taken by cargo clippy --all-targets. Fine, I wasn't using cl anyway. But that made me look at cc more carefully.

cc on macOS is a symlink to the C compiler at /usr/bin/cc. I have /opt/homebrew/opt/llvm/bin ahead in $PATH, so which cc resolves to system clang. A zsh function would shadow it — functions take precedence over $PATH lookups in interactive shells.

The argument for shadowing it anyway: I never type cc directly to invoke a compiler. Cargo, CMake, Make — they all call it programmatically.

The counter-argument: programmatic calls happen via execvp, which doesn't see shell functions. But — Rust's cc crate (used by openssl-sys, ring, zstd-sys, and a thousand other dependencies) sometimes invokes cc through shell wrappers in build scripts. The probability of hitting this is low. The debugging cost when it does happen — staring at a ring build failure that makes no sense — is high.

Renamed to cco. Three keystrokes instead of two. Worth it.

Lesson: before claiming a short command name, grep your aliases file and run type <name>. Two minutes of due diligence beats an hour of "why won't this crate build."

Decision 3: System prompt lives in a separate file

Claude Code accepts --append-system-prompt "string". Tempting to inline it in the function. Don't.

System prompts grow. Mine started as three lines (anti-sycophancy, confidence marking, counterargument-first) and is now closer to thirty. Editing thirty lines inside a shell function is painful — escaping, line continuation, no syntax highlighting for the content.

I put mine in ~/.config/claude/system-prompt.md. The function reads it at invocation:

local sys_prompt="${HOME}/.config/claude/system-prompt.md"
[[ ! -f "$sys_prompt" ]] && { echo "✗ System prompt not found: $sys_prompt"; return 1; }
local prompt_content="$(<"$sys_prompt")"
# ... later ...
claude --append-system-prompt "$prompt_content" ...

Enter fullscreen mode Exit fullscreen mode

Three benefits:

  1. Edit in your real editor. Markdown syntax highlighting. Spell check. Git diff when you tweak it.
  2. Separate from code. Different lifecycle. I commit my zsh modules to a public dotfiles repo. My system prompt I might not — it contains opinions I haven't published yet.
  3. Reload without sourcing. Edit the file, next cco invocation picks up the change. No source ~/.zshrc.

The trade-off: one more file dependency. If the file is missing, the function bails out with an error. Acceptable.

Decision 4: Subcommands instead of flags

The function dispatches on the first argument:

case "$sub" in
  plan)   ...  # read-only analysis
  safe)   ...  # dontAsk + tight whitelist
  review) ...  # ultrareview
  resume) ...  # session picker
  here)   ...  # current branch, no worktree
  run|*)  ...  # default: worktree + tmux + acceptEdits
esac

Enter fullscreen mode Exit fullscreen mode

I considered cco --plan, cco --safe, etc. Two reasons against:

  1. Flag parsing collides with Claude's flags. cco --plan could mean "wrapper's plan mode" or "pass --plan to claude" (which doesn't exist, but the parsing logic gets ambiguous fast).
  2. Subcommands compose better with tab completion. cco <Tab> shows the menu. cco --<Tab> would dump every claude flag.

The default case is run|* — bare cco or cco "some prompt" both work. The run keyword exists mostly so tab completion has something to show in the menu for the default.

There's one edge case I left in: cco "plan my vacation" would match the plan) branch because the first word is plan. If anyone ever hits this — cco run "plan my vacation" is the workaround. I judged the collision rare enough to not care.

Decision 5: --tmux in default mode

This one I want to be honest about: I almost left tmux out, because I assumed nobody would want yet another tmux session per Claude invocation.

I asked myself point-blank: do you live in tmux? Yes. Default stays tmux-on.

If you don't live in tmux, the value proposition collapses. --tmux only matters if:

  • You want to detach the session and reattach later from another shell.
  • You want multiple Claude tasks running in parallel, switchable from one terminal.
  • You SSH into your dev machine sometimes.

If none of those apply, --tmux just leaks tmux sessions. After a week of work you'll have 40 zombie sessions in tmux ls. Skip it.

I added a cleanup alias just in case:

alias cco-cleanup='tmux ls 2>/dev/null | grep "^cco-" | cut -d: -f1 | xargs -I{} tmux kill-session -t {}'

Enter fullscreen mode Exit fullscreen mode

Decision 6: Worktree by default, "here" mode as escape hatch

--worktree creates a separate git worktree per invocation. Claude works on a parallel branch in a parallel directory. Your main checkout is untouched.

The upside is real, especially on probation at a new job: Claude can refactor aggressively, and if it goes sideways, I just git worktree remove and nothing happened. No git stash, no "wait what state was I in", no panic.

The downside: sometimes you don't want isolation. You're mid-task, files open in VSCode, mental model loaded. You want Claude to fix one bug here, not in a parallel reality.

So I added a here subcommand:

here)
  shift
  local branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo 'detached')
  echo "📍 here mode — current branch ($branch), no worktree"
  claude --permission-mode acceptEdits \
         --append-system-prompt "$prompt_content" \
         "$@" 2>&1 | tee "$log_file"
  ;;

Enter fullscreen mode Exit fullscreen mode

Same system prompt, same acceptEdits, same logging. But no worktree, no tmux. Drop in, do the thing, drop out.

Use cco for "go change the auth-store architecture." Use cco here for "fix the null check on line 42."

Decision 7: caffeinate -is wrapping the default mode

macOS clamshell sleep ruins long-running agent tasks. Close the lid, fetch tea, come back — the task has been paused since you walked away.

caffeinate -is keeps the system awake (-s) and prevents idle sleep (-i) for the duration of the wrapped process. When Claude exits, caffeinate releases its assertion. No leaked state.

caffeinate -is claude --worktree "$wt_name" --tmux ... "$@"

Enter fullscreen mode Exit fullscreen mode

Honest limitation: caffeinate -s only works while on AC power. Apple's SMC enforces clamshell sleep on battery regardless of what userland says. There's no way around it without third-party kexts I would never install.

So: lid closed + AC + (external display OR keyboard) → works via standard clamshell mode. Lid closed + battery → sleeps no matter what. I tell people up front, because the alternative is them thinking the wrapper is broken.

I only added caffeinate to the default mode, not to plan, here, safe, or review. Reasoning: the other modes are short-lived. Default mode (worktree + long refactor) is where caffeination earns its keep.

Decision 8: Tee everything, except interactive pickers

Each invocation logs to ~/claude-logs/<timestamp>_<projectname>.log via tee:

claude ... "$@" 2>&1 | tee "$log_file"

Enter fullscreen mode Exit fullscreen mode

This gives me a searchable history without relying on Claude's internal session storage. When something goes wrong three days later — "wait, what did Claude say about the auth refactor on Tuesday" — I rg the logs.

Exception: cco resume uses Claude's interactive session picker. Piping that through tee breaks the picker's TUI rendering. No log for resume. I considered fixing it with script(1) but that's a yak shave for a feature I'd use rarely.

What I'd do differently

If I were starting over:

  1. Pick the name first, by elimination, not by aspiration. I lost 15 minutes flip-flopping between cc, cl, and cco. Running type <candidate> against four options up front would have settled it immediately.
  2. Write the system prompt before the wrapper. The wrapper is plumbing. The system prompt is the actual leverage — that's where you tell Claude how to think. I built the wrapper first because it was the fun part. Wrong order.
  3. Don't add features speculatively. I almost added a --wide flag for --add-dir to pull in shared types and notes directories. I cut it before writing it. Six months in, I still don't need it. Good cut.

The wrapper

Full code: gist.github.com/IgorKramar/9b4c698909047934ee8e5dd775e94ebc

If you build something similar, you'll make different decisions. Some of mine were context-specific (probation at a new job → worktree isolation matters more), some are tooling-specific (tmux user → --tmux default). The point isn't to copy the code. The point is: when your wrapper hits 60 lines, every line should be a deliberate choice, not a default someone else's tutorial gave you.