{"id":903,"date":"2026-03-03T14:05:39","date_gmt":"2026-03-03T06:05:39","guid":{"rendered":"https:\/\/vinta.ws\/code\/?p=903"},"modified":"2026-09-10T00:34:27","modified_gmt":"2026-09-09T16:34:27","slug":"claude-code-useful-plugins-skills-and-mcps","status":"publish","type":"post","link":"https:\/\/vinta.ws\/code\/claude-code-useful-plugins-skills-and-mcps.html","title":{"rendered":"Claude Code and Codex: Things I Learned After Using Them Every Day"},"content":{"rendered":"<p>I've used Claude Code and Codex daily since they came out. Here are the best practices, tools, and configuration patterns that work for me. Most of them apply to both coding agents.<\/p>\n<blockquote>\n<p>TL;DR<br \/>\nMy opinionated setup for Claude Code:<br \/>\n<a href=\"https:\/\/github.com\/vinta\/hal-9000\">https:\/\/github.com\/vinta\/hal-9000<\/a><\/p>\n<\/blockquote>\n<h2>CLAUDE.md<\/h2>\n<h3>The Global CLAUDE.md<\/h3>\n<p>Your <code>~\/.claude\/CLAUDE.md<\/code> should only contain:<\/p>\n<ul>\n<li>Your preferences and rules to correct agent behavior<\/li>\n<li>You probably don't need to tell it YAGNI or KISS as bare principles. They're already built in.<\/li>\n<\/ul>\n<p>Pro tip 1: before adding something to <code>CLAUDE.md<\/code>, ask it, <strong>&quot;Is this already covered in your system prompt?&quot;<\/strong><br \/>\nPro tip 2: try my <a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/skills\/refactor-claude-md\/SKILL.md\">refactor-claude-md<\/a> or <a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/skills\/refactor-agents-md\/SKILL.md\">refactor-agents-md<\/a> skill!<\/p>\n<p>Here are some parts of my <code>CLAUDE.md<\/code> I found useful:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-markdown\">## Communication Style\n\n- Push back when something seems off. Challenge premises, question assumptions, propose simpler alternatives\n- Before a non-trivial change (multiple files, new behavior), outline your approach in 3-5 bullets (what, in what order), then execute without asking. For a small edit, one sentence of intent is enough\n\n### Surface Assumptions\n\nName each assumption you resolved by guessing as its own bullet, so the user can catch what they forgot to tell you.\n\nWhen the user asks for advice or a recommendation, first surface the assumptions their question takes for granted and the missing information that would change your answer (and how), so they can catch the framing they got wrong.\n\n### Use AskUserQuestion\n\nWhen you ask the user anything whose answer is a selection rather than a sentence (multiple-choice, yes\/no questions whether they gate next steps or offer optional follow-up work, picking from a list, choosing between approaches), ask with the <code>AskUserQuestion<\/code> tool, so the user clicks an option instead of typing. This holds inside skills: a skill that prescribes its own question format decides what you ask, not how.\n\nWhen presenting approaches, put the summary in each option's label and the pros\/cons in its description.\n\n### Prefer Online Sources\n\nTraining data goes stale: library\/framework\/SDK APIs, config keys, CLI flags, cloud services, platform features, syntax, and versions change, and guessing has repeatedly cost debugging round-trips.\n\nInvoke the <code>find-docs<\/code> skill BEFORE writing code or config that touches any of those, and BEFORE answering questions about them. Being about to write such code is trigger enough, even when no question was asked. Confidence is not an exemption, and neither is the library being well known. Answering from training data, or fetching a remembered docs URL instead of invoking the skill, does not satisfy this rule. For topics <code>find-docs<\/code> covers poorly, <code>WebFetch<\/code> the official docs instead of falling back to training data.\n\nIf the user provides URLs, <code>WebFetch<\/code> each one as a primary source before searching further.<\/code><\/pre>\n<p>Also see:<\/p>\n<ul>\n<li><a href=\"https:\/\/raw.githubusercontent.com\/vinta\/hal-9000\/refs\/heads\/main\/dotfiles\/.claude\/CLAUDE.md\">GitHub: vinta\/hal-9000 - The user-level CLAUDE.md<\/a><\/li>\n<li><a href=\"https:\/\/www.aihero.dev\/a-complete-guide-to-agents-md\">A Complete Guide To AGENTS.md<\/a><\/li>\n<\/ul>\n<h3>The Project CLAUDE.md<\/h3>\n<p>For project-specific instructions, put them in the project-level <code>CLAUDE.md<\/code>.<\/p>\n<p>The highest-signal content in your project <code>CLAUDE.md<\/code> (or any skill) is the <strong>Gotchas<\/strong> section. Build these from the failure points Claude Code actually runs into.<\/p>\n<p>Also see:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/skills\/refactor-claude-md\/SKILL.md\">GitHub: vinta\/hal-9000 - The <code>refactor-claude-md<\/code> skill<\/a><\/li>\n<\/ul>\n<h2>Per File Type Rules<\/h2>\n<p>For language-specific or per-file <a href=\"https:\/\/code.claude.com\/docs\/en\/memory#organize-rules-with-claude\/rules\/\">rules<\/a>, put them in <code>~\/.claude\/rules\/<\/code>, so Claude Code only loads them when editing those file types.<\/p>\n<p>For instance, <code>~\/.claude\/rules\/python.md<\/code>:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-markdown\">---\npaths:\n  - \"**\/*.py\"\n  - \"**\/pyproject.toml\"\n---\n\n# Python\n\n- Check https:\/\/awesome-python.com\/llms.txt before choosing a library or tool\n- Prefer the standard library over adding a dependency \u2014 <code>tomllib<\/code> over <code>tomli<\/code>, <code>pathlib<\/code> over external path libs\n  - <code>requests<\/code> is fine since it's the de facto standard\n- Version specifiers in <code>pyproject.toml<\/code>: <code>&gt;=<\/code> floors (uv's <code>add-bounds<\/code> default). Reproducibility lives in <code>uv.lock<\/code> + <code>uv sync --locked<\/code>; <code>==<\/code> pins there would duplicate the lockfile and block <code>uv lock --upgrade<\/code>\n  - Pin <code>==<\/code> only where no lockfile exists (standalone scripts, requirements.txt)\n- Use <code>uv<\/code> for project and environment management; <code>uv run<\/code> instead of <code>python3<\/code>\n  - Projects with a <code>[build-system]<\/code> need <code>no-build = false<\/code> in <code>[tool.uv]<\/code> \u2014 the global <code>no-build = true<\/code> in <code>~\/.config\/uv\/uv.toml<\/code> merges down and blocks the editable install\n- Use <code>pytest<\/code> for testing\n- Use <code>ruff<\/code> for linting and formatting\n  - Outside tests, <code>assert<\/code> needs <code># noqa: S101 assert<\/code>\n- When the linter flags something, read the rule (<code>ruff rule &lt;CODE&gt;<\/code>) and fix the code. Suppress with <code># noqa<\/code> only when the rule does not apply to the project\n  - Every <code># noqa<\/code> includes the rule name: <code># noqa: S603 subprocess-without-shell-equals-true<\/code>, or <code># noqa: S603 PLW1510 subprocess-without-shell-equals-true subprocess-run-without-check<\/code> for multiple rules\n- Use <code>ty<\/code> for type checking\n- Use <code>TypedDict<\/code>, not plain dicts, for dict shapes crossing a JSON boundary<\/code><\/pre>\n<p>The full rules I have:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/tree\/main\/dotfiles\/.claude\/rules\">GitHub: vinta\/hal-9000 - .claude\/rules\/<\/a><\/li>\n<\/ul>\n<h2>Output Styles<\/h2>\n<p>Claude Code provides a built-in method to modify the system prompt to change how Claude responds: <a href=\"https:\/\/code.claude.com\/docs\/en\/output-styles\">Output styles<\/a>. You can also write your own. For instance, my <a href=\"https:\/\/github.com\/vinta\/hal-9000\/tree\/main\/plugins\/hal-output-styles\">Say no more<\/a>, inspired by <a href=\"https:\/\/github.com\/JuliusBrussee\/caveman\">caveman<\/a>, so every reply drops articles, filler, and pleasantries while keeping every technical detail:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-markdown\">---\nname: Say no more\ndescription: Nudge nudge. Know what I mean? Say no more\nkeep-coding-instructions: true\n---\n\nWrite telegraphically, as if every word cost money. All technical substance stays. Only fluff dies.\n\n## Rules\n\n### Shape\n\nLead with the answer. The first sentence carries the verdict or result; the reason comes after, never before.\nPattern: <code>[thing] [action] [reason]. [next step].<\/code>\nState each fact once; never restate the same fact in a second form.\n\n### Cut\n\nUse one word when one word is enough.\nRemove all mannered prose.\nPrefer the common word over the literary one (name, not coin; only, not solely).\nDrop articles (a\/an\/the), filler (just\/really\/basically\/actually\/simply), pleasantries (sure\/certainly\/happy to), hedging, decorative tables and emoji, and causal arrows (\u2192).\nFragments are fine. Use short synonyms (fix, not \"implement a solution for\"). Standard acronyms are fine (DB\/API\/HTTP).\n\n### Keep exact\n\nNever drop not\/never\/no\/only\/except: a flipped meaning is worse than any token saved.\nKeep numbers, units, and technical terms exact. Never invent abbreviations (cfg\/impl\/req\/res\/fn).\nCode blocks, commands, API names, and error strings stay byte-exact, never compressed. For a long error log, quote the shortest decisive line, not the whole dump.\n\n### Example\n\nNot: \"Sure! I'd be happy to help. The issue is most likely caused by your auth middleware not validating token expiry.\"\nYes: \"Bug in auth middleware. Token expiry check use <code>&lt;<\/code> not <code>&lt;=<\/code>. Fix:\"\n\n### Agentic turns\n\nFire tool calls directly, with no progress narration before or between calls.\nCLAUDE.md duties survive compressed, never dropped: pre-change outline bullets, named-assumption bullets, and findings the user needs. Write them telegraphically too.<\/code><\/pre>\n<p>Why an output style instead of the global <code>CLAUDE.md<\/code>? They land in different places: an output style becomes part of the system prompt, and Claude Code periodically reminds the model to stick to it mid-conversation, while <code>CLAUDE.md<\/code> gets injected as a user message, where it competes with all your other rules. Plus, you can switch styles in <code>\/config<\/code> without touching your global rules.<\/p>\n<p>One gotcha: output styles apply to the main conversation only.<\/p>\n<p>Also see:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/plugins\/hal-output-styles\/output-styles\/say-no-more.md\">GitHub: vinta\/hal-9000 - say-no-more.md<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/plugins\/hal-output-styles\/output-styles\/asd-ste100.md\">GitHub: vinta\/hal-9000 - asd-ste100.md<\/a><\/li>\n<\/ul>\n<h2>Configurations<\/h2>\n<h3>Settings<\/h3>\n<p>There are some useful <a href=\"https:\/\/code.claude.com\/docs\/en\/settings\">configurations<\/a> you could set in your <code>~\/.claude\/settings.json<\/code>:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-json\">{\n  \"$schema\": \"https:\/\/json.schemastore.org\/claude-code-settings.json\",\n  \"env\": {\n    \"CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY\": \"1\",\n    \"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS\": \"1\",\n    \"DISABLE_ERROR_REPORTING\": \"1\",\n    \"DISABLE_EXTRA_USAGE_COMMAND\": \"1\",\n    \"DISABLE_FEEDBACK_COMMAND\": \"1\",\n    \"DISABLE_UPGRADE_COMMAND\": \"1\"\n  },\n  \"permissions\": {\n    \"allow\": [\"...\"],\n    \"deny\": [\"...\"],\n    \"ask\": [\"...\"],\n    \"defaultMode\": \"auto\"\n  },\n  \"model\": \"claude-fable-5-1[1m]\",\n  \"effortLevel\": \"high\",\n  \"advisorModel\": \"fable\",\n  \"cleanupPeriodDays\": 999999,\n  \"includeGitInstructions\": false,\n  \"showClearContextOnPlanAccept\": true,\n  \"teammateMode\": \"auto\",\n  \"voice\": { \"enabled\": true }\n}<\/code><\/pre>\n<p>Highlights:<\/p>\n<ul>\n<li><code>&quot;CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS&quot;: &quot;1&quot;<\/code>: Enable <a href=\"https:\/\/code.claude.com\/docs\/en\/agent-teams\">Agent Team<\/a> feature, a fancy way to consume a huge amount of tokens<\/li>\n<li><code>&quot;permissions.defaultMode&quot;: &quot;auto&quot;<\/code>: Use this to pretend it's safer than <code>--dangerously-skip-permissions<\/code><\/li>\n<li><code>&quot;advisorModel&quot;: &quot;fable&quot;<\/code>: Use something faster like <code>sonnet<\/code> as the main model, and let it <a href=\"https:\/\/code.claude.com\/docs\/en\/advisor\">consult<\/a> <code>fable<\/code> when needed<\/li>\n<li><code>&quot;includeGitInstructions&quot;: false<\/code>: Remove built-in commit\/PR instructions and git status snapshot from the system prompt, since my <a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/skills\/commit\/SKILL.md\">commit<\/a> skill covers that<\/li>\n<li><code>&quot;cleanupPeriodDays&quot;: 999999<\/code>: By default, your chat history (location: <code>~\/.claude\/projects\/<\/code>) will be deleted after 30 days<\/li>\n<li><code>&quot;voice&quot;: { &quot;enabled&quot;: true }<\/code>: Enable <a href=\"https:\/\/code.claude.com\/docs\/en\/voice-dictation\">Voice Dictation<\/a> feature. Code like a boss!<\/li>\n<\/ul>\n<p>I turned all this settings-tweaking into a skill: <a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/skills\/audit-claude-settings\/SKILL.md\">audit-claude-settings<\/a> fetches the latest settings and env-vars from official docs, diffs them against your actual config, and suggests changes tied to how you work.<\/p>\n<p>The full settings I use:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/dotfiles\/.claude\/settings.json\">GitHub: vinta\/hal-9000 - .claude\/settings.json<\/a><\/li>\n<\/ul>\n<h3>Permissions<\/h3>\n<p>If you're not using a sandbox or devcontainer for Claude Code, you may want to block some <strong>evil<\/strong> commands in your <code>~\/.claude\/settings.json<\/code>:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-json\">{\n  \"permissions\": {\n    \"defaultMode\": \"auto\",\n    \"deny\": [\n      \"Read(~\/.aws\/**)\",\n      \"Read(~\/.config\/**)\",\n      \"Read(~\/.docker\/**)\",\n      \"Read(~\/.dropbox\/**)\",\n      \"Read(~\/.gnupg\/**)\",\n      \"Read(~\/.gsutil\/**)\",\n      \"Read(~\/.kube\/**)\",\n      \"Read(~\/.npmrc)\",\n      \"Read(~\/.orbstack\/**)\",\n      \"Read(~\/.pypirc)\",\n      \"Read(~\/.ssh\/**)\",\n      \"Read(~\/*_history)\",\n      \"Read(~\/**\/*credential*)\",\n      \"Read(~\/Library\/**)\",\n      \"Edit(~\/Library\/**)\",\n      \"Read(~\/Dropbox\/**)\",\n      \"Edit(~\/Dropbox\/**)\",\n      \"Read(\/\/etc\/**)\",\n      \"Edit(\/\/etc\/**)\",\n      \"Bash(git -c *)\",\n      \"Bash(git --config-env*)\",\n      \"Bash(git --git-dir*)\",\n      \"Bash(gh repo delete *)\",\n      \"Bash(su *)\",\n      \"Bash(sudo *)\",\n      \"Bash(passwd *)\",\n      \"Bash(env *)\",\n      \"Bash(printenv *)\",\n      \"Bash(history *)\",\n      \"Bash(fc *)\",\n      \"Bash(eval *)\",\n      \"Bash(exec *)\",\n      \"Bash(rsync *)\",\n      \"Bash(sftp *)\",\n      \"Bash(telnet *)\",\n      \"Bash(socat *)\",\n      \"Bash(nc *)\",\n      \"Bash(ncat *)\",\n      \"Bash(netcat *)\",\n      \"Bash(nmap *)\",\n      \"Bash(chflags *)\",\n      \"Bash(xattr *)\",\n      \"Bash(diskutil *)\",\n      \"Bash(mkfs *)\",\n      \"Bash(security *)\",\n      \"Bash(defaults *)\",\n      \"Bash(launchctl *)\",\n      \"Bash(osascript *)\",\n      \"Bash(dscl *)\",\n      \"Bash(networksetup *)\",\n      \"Bash(scutil *)\",\n      \"Bash(systemsetup *)\",\n      \"Bash(pmset *)\",\n      \"Bash(crontab *)\"\n    ],\n    \"ask\": [\n      \"Bash(open *)\",\n      \"Bash(chmod *)\",\n      \"Bash(chown *)\",\n      \"Bash(kill *)\",\n      \"Bash(killall *)\",\n      \"Bash(pkill *)\",\n      \"Bash(curl *-d *)\",\n      \"Bash(curl *--data*)\",\n      \"Bash(curl *--json *)\",\n      \"Bash(curl *-F *)\",\n      \"Bash(curl *--form *)\",\n      \"Bash(curl *-T *)\",\n      \"Bash(curl *--upload-file *)\",\n      \"Bash(curl *-H *)\",\n      \"Bash(curl *--header *)\",\n      \"Bash(brew install *)\",\n      \"Bash(pip install *)\",\n      \"Bash(uv pip install *)\",\n      \"Bash(uv tool install *)\",\n      \"Bash(uv add *)\",\n      \"Bash(npm install *)\",\n      \"Bash(npm i *)\",\n      \"Bash(yarn add *)\",\n      \"Bash(pnpm add *)\",\n      \"Bash(bun add *)\",\n      \"Bash(git push *)\",\n      \"Bash(git remote add *)\",\n      \"Bash(git remote set-url *)\",\n      \"Bash(git config remote.*)\",\n      \"Bash(git config * remote.*)\",\n      \"Bash(gh repo create *)\",\n      \"Bash(gh repo rename *)\",\n      \"Bash(gh *--admin*)\",\n      \"Bash(gh api *-X *)\",\n      \"Bash(gh api *--method *)\"\n    ]\n  },\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"python3 ~\/.claude\/hooks\/guard-bash-paths.py\"\n          },\n          {\n            \"type\": \"command\",\n            \"command\": \"python3 ~\/.claude\/hooks\/guard-network-egress.py\"\n          }\n        ]\n      }\n    ]\n  }\n}<\/code><\/pre>\n<p>Rule precedence catches people out: Claude Code evaluates <code>deny<\/code>, then <code>ask<\/code>, then <code>allow<\/code>, and the first match wins \u2014 specificity never reorders them. So a narrow <code>&quot;allow&quot;: [&quot;Bash(curl https:\/\/code.claude.com\/docs\/*)&quot;]<\/code> does nothing while <code>&quot;ask&quot;: [&quot;Bash(curl *)&quot;]<\/code> is present, and a <code>PreToolUse<\/code> hook returning <code>allow<\/code> doesn't rescue it either, since a matching ask rule still prompts. Bash patterns have no negation, so carving an exception out of a broad ask rule means narrowing the ask rule itself.<\/p>\n<p>Also, <code>&quot;deny&quot;: [&quot;Read(~\/.aws\/**)&quot;, &quot;Read(~\/.kube\/**)&quot;, ...]<\/code> alone is not enough, since Claude Code can still read sensitive files through the <code>Bash<\/code> tool. You can write a simple hook to intercept <code>Bash<\/code> commands that access blocked files, like this <a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/dotfiles\/.claude\/hooks\/guard-bash-paths.py\"><code>guard-bash-paths.py<\/code><\/a> hook. However, <strong>Claude Code can still write scripts to read sensitive data<\/strong> and bypass all of the above defenses. The safest approach is using sandbox after all.<\/p>\n<h2>Plugins<\/h2>\n<p>Claude Code <a href=\"https:\/\/code.claude.com\/docs\/en\/discover-plugins\">Plugins<\/a> are simply a way to package skills, commands, agents, hooks, MCP servers, LSP servers, and monitors. Distributing them as a plugin has the following advantages:<\/p>\n<ul>\n<li>Auto update (versioned releases)<\/li>\n<li>Auto hooks configuration (users don't need to edit their <code>~\/.claude\/settings.json<\/code> manually)<\/li>\n<li>Skills have a <code>\/plugin-name:your-skill-name<\/code> prefix (no more conflicts)<\/li>\n<\/ul>\n<p>To install a plugin, you need to add a marketplace first. A marketplace is usually just a GitHub repo. Think of it as a namespace.<\/p>\n<pre class=\"line-numbers\"><code class=\"language-bash\">\/plugin marketplace add mattpocock\/skills\n\/plugin marketplace add vinta\/hal-9000\n\n# browse plugins\n\/plugins<\/code><\/pre>\n<p>Recommended:<\/p>\n<ul>\n<li><del><a href=\"https:\/\/github.com\/openai\/codex-plugin-cc\">GitHub: openai\/codex-plugin-cc<\/a>: OpenAI Codex's official plugin for Claude Code<\/del><\/li>\n<li><del><a href=\"https:\/\/github.com\/xai-org\/grok-build-plugin-cc\">GitHub: xai-org\/grok-build-plugin-cc<\/a>: xAI Grok Build's official plugin for Claude Code<\/del><\/li>\n<li><a href=\"https:\/\/github.com\/mattpocock\/skills\">GitHub: mattpocock\/skills<\/a>: This is my favorite skill set<\/li>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/tree\/main\/skills\">GitHub: vinta\/hal-9000<\/a>: My agentic skills sharpened by daily use<\/li>\n<\/ul>\n<h2>Skills<\/h2>\n<p><a href=\"https:\/\/code.claude.com\/docs\/en\/skills\">Skills<\/a> can contain executable scripts and hooks, not just Markdown. <strong>Use with caution!<\/strong> When in doubt, have your agent review them first.<\/p>\n<p>Here are skills I've used, mostly installed per project when needed:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-bash\"># my skills\nnpx skills add https:\/\/github.com\/vinta\/hal-9000 \\\n--skill commit \\\n--skill pr \\\n--skill fuck-over-engineering \\\n--skill best-practices \\\n--skill blindspot \\\n--skill simple-english \\\n--skill write-like-me \\\n--skill audit-claude-settings \\\n--skill refactor-claude-md \\\n--skill refactor-agents-md \\\n--skill refactor-memory \\\n--skill refactor-skill \\\n--skill update-allowed-tools \\\n--agent codex \\\n-g\n\n# workflow skills\nnpx skills add https:\/\/github.com\/mattpocock\/skills \\\n--skill code-review \\\n--skill codebase-design \\\n--skill diagnosing-bugs \\\n--skill domain-modeling \\\n--skill grill-me \\\n--skill grill-with-docs \\\n--skill grilling \\\n--skill handoff \\\n--skill implement \\\n--skill improve-codebase-architecture \\\n--skill prototype \\\n--skill research \\\n--skill tdd \\\n--skill teach \\\n--skill to-spec \\\n--skill to-tickets \\\n--skill wait-what \\\n--skill wayfinder \\\n--skill writing-for-agents \\\n--agent codex \\\n-g\n\n# doc skills\nnpx skills add https:\/\/github.com\/upstash\/context7 --skill find-docs --agent codex claude-code -g\nnpx skills add https:\/\/github.com\/humanlayer\/skills --skill show-me --agent codex claude-code -g\n\n# language skills\nnpx skills add https:\/\/github.com\/dagster-io\/skills --skill dignified-python\nnpx skills add https:\/\/github.com\/cursor\/plugins --skill typescript-best-practices\nnpx skills add https:\/\/github.com\/JetBrains\/go-modern-guidelines --skill use-modern-go\n\n# backend skills\nnpx skills add https:\/\/github.com\/vintasoftware\/django-ai-plugins\nnpx skills add https:\/\/github.com\/google\/skills\nnpx skills add https:\/\/github.com\/cloudflare\/skills\nnpx skills add https:\/\/github.com\/planetscale\/database-skills\nnpx skills add https:\/\/github.com\/supabase\/agent-skills\n\n# frontend skills\nnpx skills add https:\/\/github.com\/millionco\/react-doctor\nnpx skills add https:\/\/github.com\/vercel-labs\/agent-skills\nnpx skills add https:\/\/github.com\/vercel-labs\/next-skills\n\n# design skills\nnpx skills add https:\/\/github.com\/pbakaus\/impeccable\n\n# video skills\nnpx skills add https:\/\/github.com\/remotion-dev\/skills\nnpx skills add https:\/\/github.com\/AmanVarshney01\/tcut\n\n# browser skills\nnpx skills add https:\/\/github.com\/microsoft\/playwright-cli --agent codex claude-code -g\n\nnpx skills list -g\nnpx skills update -g\nnpx skills remove --all -g<\/code><\/pre>\n<p>Recommended:<\/p>\n<ul>\n<li><code>\/wayfinder<\/code> from <a href=\"https:\/\/github.com\/mattpocock\/skills\">mattpocock<\/a>: Let AI ask you a lot of questions until you get annoyed<\/li>\n<li><code>\/find-docs<\/code> from <a href=\"https:\/\/github.com\/upstash\/context7\">context7<\/a>: Find the latest documentations<\/li>\n<li><code>\/impeccable<\/code> from <a href=\"https:\/\/github.com\/pbakaus\/impeccable\">impeccable<\/a>: The better version of the official <code>\/frontend-design<\/code> skill<\/li>\n<li><code>\/fuck-over-engineering<\/code> from <a href=\"https:\/\/github.com\/vinta\/hal-9000#skills\">hal-9000<\/a>: Run it often, you will like it<\/li>\n<\/ul>\n<p>You can find more skills on <a href=\"https:\/\/www.skills.sh\/\">skills.sh<\/a>.<\/p>\n<h2>MCP Servers<\/h2>\n<p>You probably don't need any <a href=\"https:\/\/code.claude.com\/docs\/en\/mcp\">MCP servers<\/a> if you can do the same thing with CLI + skills.<\/p>\n<h3>Context7 MCP<\/h3>\n<p>No, just use the <code>ctx7<\/code> CLI with <code>find-docs<\/code> skill instead.<\/p>\n<pre class=\"line-numbers\"><code class=\"language-bash\">npm install -g ctx7\nnpx skills add https:\/\/github.com\/upstash\/context7 --skill find-docs --agent codex claude-code -g<\/code><\/pre>\n<ul>\n<li><a href=\"https:\/\/github.com\/upstash\/context7\">GitHub: upstash\/context7<\/a><\/li>\n<\/ul>\n<h3>Playwright MCP<\/h3>\n<p>No, you should use the <code>playwright-cli<\/code> skill instead. The tool supports <strong>headed mode<\/strong> (the opposite of headless), if you'd like to see the browser.<\/p>\n<pre class=\"line-numbers\"><code class=\"language-bash\">npm install -g @playwright\/cli\nplaywright-cli install-browser\nnpx skills add https:\/\/github.com\/microsoft\/playwright-cli --skill playwright-cli --agent codex claude-code -g<\/code><\/pre>\n<ul>\n<li><a href=\"https:\/\/github.com\/microsoft\/playwright-cli\">GitHub: microsoft\/playwright-cli<\/a><\/li>\n<\/ul>\n<h3>GitHub MCP<\/h3>\n<p>No, you should use the <code>gh<\/code> command instead.<\/p>\n<pre class=\"line-numbers\"><code class=\"language-bash\">brew install gh<\/code><\/pre>\n<h2>Hooks<\/h2>\n<p>Both <a href=\"https:\/\/code.claude.com\/docs\/en\/hooks\">Claude Code<\/a> and <a href=\"https:\/\/learn.chatgpt.com\/docs\/hooks\">Codex<\/a> support hooks. Hooks make Claude Code run specific commands on lifecycle events like <code>SessionStart<\/code>, <code>UserPromptSubmit<\/code>, and <code>PreToolUse<\/code>.<\/p>\n<p>Instead of reminding Claude Code to run the linter or tests in your prompts (and it still forgets sometimes), just write a <code>PostToolUse<\/code> hook that runs deterministically:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-json\">{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Write|Edit\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"uv run ruff check . &gt;&amp;2 || exit 2\",\n            \"if\": \"Edit(**\/*.py)\",\n            \"timeout\": 30,\n            \"statusMessage\": \"Linting Python code...\"\n          }\n        ]\n      }\n    ]\n  }\n}<\/code><\/pre>\n<p>It's worth noting that only exit code <code>2<\/code> blocks and feeds the output back to Claude, and only through <strong><code>stderr<\/code><\/strong> \u2014 while tools like <code>ruff<\/code> and <code>ty<\/code> print their diagnostics to <strong><code>stdout<\/code><\/strong>. A bare <code>uv run ruff check .<\/code> exits <code>1<\/code>, a non-blocking error, so Claude only gets the first line of <code>stderr<\/code>, never the lint errors. <code>&gt;&amp;2<\/code> moves them to the stream Claude reads, and <code>|| exit 2<\/code> makes the hook actually block.<\/p>\n<p>Also, do the path matching in <code>if<\/code>, not inside a wrapper script. <code>if<\/code> takes the same <a href=\"https:\/\/code.claude.com\/docs\/en\/permissions\">permission rule syntax<\/a>.<\/p>\n<p>I also wrote some Claude Code plugins that use hooks:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/tree\/main\/plugins\/hal-session-auto-rename\">GitHub: vinta\/hal-9000 - hal-session-auto-rename<\/a>: Automatically name each session, and optionally rename it as the conversation evolves<\/li>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/tree\/main\/plugins\/hal-voice\">GitHub: vinta\/hal-9000 - hal-voice<\/a>: Play HAL 9000 voice clips on Claude Code hook events<\/li>\n<\/ul>\n<p>For example, <a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/plugins\/hal-session-auto-rename\/scripts\/hal-session-auto-rename.py#L50\">hal-session-auto-rename<\/a>. Since Claude can <a href=\"https:\/\/code.claude.com\/docs\/en\/cross-session-messaging\">message your other Claude Code sessions<\/a> by name (you could also explicitly mention them with <code>@session-name<\/code>), a good session name actually matters. I found Claude Code already titles every session once, from its first real prompt, and stores it in the transcript, so I just wired that up to a <code>UserPromptSubmit<\/code> hook, which can set <code>sessionTitle<\/code>.<\/p>\n<h2>Useful Tips<\/h2>\n<h3>Prompt Best Practices<\/h3>\n<ul>\n<li><a href=\"https:\/\/platform.claude.com\/docs\/en\/build-with-claude\/prompt-engineering\/claude-prompting-best-practices\">Claude Prompting Best Practices<\/a><\/li>\n<li><a href=\"https:\/\/developers.openai.com\/api\/docs\/guides\/latest-model\">Codex Prompting Best Practices<\/a><\/li>\n<\/ul>\n<h3>Command Aliases<\/h3>\n<pre class=\"line-numbers\"><code class=\"language-bash\"># in ~\/.zshrc\nalias cc=\"claude\"\nalias ccmax=\"claude --model fable --effort max\"\nalias ccfable=\"claude --model fable\"\nalias ccopus=\"claude --model opus\"\nalias ccsonnet=\"claude --model sonnet\"\nalias ccyolo=\"claude --dangerously-skip-permissions\"\nccp() { claude --model sonnet --effort high --safe-mode --no-session-persistence --no-chrome -p \"$*\"; }\n\nalias cx='codex'\nalias cxultra='codex --model gpt-6-astra --config model_reasoning_effort=ultra'\nalias cxyolo='codex --dangerously-bypass-approvals-and-sandbox'<\/code><\/pre>\n<p>Use <code>ccp<\/code> for ad-hoc prompts:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-bash\">ccp \"commit\"\nccp \"list all .md in this repo\"<\/code><\/pre>\n<ul>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/dotfiles\/.zshrc\">GitHub: vinta\/hal-9000 - .zshrc<\/a><\/li>\n<\/ul>\n<h3>Customize Your Statusline<\/h3>\n<p>Claude Code has a customizable <a href=\"https:\/\/code.claude.com\/docs\/en\/statusline\">statusline<\/a> at the bottom of the terminal. You can run any script that outputs text.<\/p>\n<p>Mine shows the current model, the current working folder, the git branch, and a grammar-corrected version of my last prompt (because my English needs all the help it can get). The grammar correction runs an ad-hoc <code>claude<\/code> command inside the statusline script.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/vinta\/hal-9000\/main\/assets\/claude-code-statusline-grammar-check.png\" alt=\"Claude Code Statusline with English Grammar Check example\" \/><\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/tree\/main\/plugins\/hal-statusline\">GitHub: vinta\/hal-9000 - statusline<\/a><\/li>\n<\/ul>\n<h3>Run Ad-Hoc Claude Commands Inside Scripts<\/h3>\n<p>You can invoke <code>claude<\/code> as a one-shot CLI tool from hooks, statusline scripts, CI, or anywhere else. The trick is using the right flags to get a clean, isolated call with zero side effects:<\/p>\n<pre class=\"line-numbers\"><code class=\"language-python\">cmd = \"\"\"\n    claude\n    --model haiku\n    --max-turns 1\n    --setting-sources \"\"\n    --tools \"\"\n    --disable-slash-commands\n    --no-session-persistence\n    --no-chrome\n    --safe-mode\n    --print\n\"\"\"\n\nresult = subprocess.run(\n    [*shlex.split(cmd), your_prompt],\n    capture_output=True,\n    text=True,\n    timeout=15,\n    cwd=\"\/tmp\",\n)<\/code><\/pre>\n<p>What each flag does:<\/p>\n<ul>\n<li><code>--setting-sources &quot;&quot;<\/code>: don't load hooks (avoids infinite recursion if called from a hook)<\/li>\n<li><code>--no-session-persistence<\/code> and <code>cwd=&quot;\/tmp&quot;<\/code>: avoid polluting your current context<\/li>\n<li><code>--tools &quot;&quot;<\/code>: no file access, no bash, pure text in\/out<\/li>\n<li><code>--no-chrome<\/code>: skip the Chrome integration<\/li>\n<li><code>--safe-mode<\/code>: prevent loading CLAUDE.md, skills, plugins, MCP, or auto-memory<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>I've used Claude Code daily since it came out. Here are the best practices, tools, and configuration patterns I've picked up. Most of this applies to Codex too.<\/p>\n","protected":false},"author":1,"featured_media":904,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[97],"tags":[127,153,101,154],"class_list":["post-903","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-about-ai","tag-chat-bot","tag-claude-code","tag-cli-tool","tag-coding-agent"],"_links":{"self":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/903","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/comments?post=903"}],"version-history":[{"count":0,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/903\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media\/904"}],"wp:attachment":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media?parent=903"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/categories?post=903"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/tags?post=903"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}