{"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-08-27T00:30:56","modified_gmt":"2026-08-26T16:30:56","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: Things I Learned After Using It Every Day"},"content":{"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<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 nudges to correct agent behaviors<\/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> 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### Surface Assumptions\n\nProceeding on a guess is right; proceeding silently is not.\n\nWhere my request left a decision open and you resolved it by guessing, name that assumption as its own bullet so I can catch what I forgot to tell you.\n\nWhen I ask for advice or a recommendation, first surface the assumptions my question takes for granted and the missing information that would change your answer (and how), so I can catch the framing I 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 instead of plain text, so the user clicks an option instead of typing.\n\nWhen presenting approaches, put the summary in each option's label and the pros\/cons in its description. Plain text is fine when the answer is open-ended. This changes the format of questions, not whether to ask: never use it to ask permission for work you already have enough information to do.\n\n## Workflow\n\n- Before proposing a design of your own, invoke the <code>best-practices<\/code> skill to study prior art. Assume prior art exists; spend original design only where your problem actually differs\n- When a finding invalidates the approach you're executing (contradicts it, or makes it unnecessary), stop and lead with it: what it kills, what the plan is now. Mentioning it in passing while continuing does not count\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.\n\nIf the user provides URLs, <code>WebFetch<\/code> each one as a primary source before searching further. Never skip user-provided URLs. For topics <code>find-docs<\/code> covers poorly, <code>WebFetch<\/code> the official docs instead of falling back to training data.<\/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:\/\/x.com\/trq212\/status\/2033949937936085378\">Twitter: @trq212 - Lessons from Building Claude Code: How We Use Skills<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/anthropics\/claude-plugins-official\/blob\/main\/plugins\/claude-md-management\/skills\/claude-md-improver\/references\/templates.md\">GitHub: anthropics\/claude-plugins-official - CLAUDE.md templates<\/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- When choosing a Python library or tool, check https:\/\/awesome-python.com\/llms.txt before picking one\n- Prefer the standard library over adding a dependency (supply-chain hardening) \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>: use <code>&gt;=<\/code> floors (uv's <code>add-bounds<\/code> default)\n  - Exact reproducibility lives in <code>uv.lock<\/code> + <code>uv sync --locked<\/code>, so <code>==<\/code> pins in <code>pyproject.toml<\/code> would only duplicate the lockfile and block <code>uv lock --upgrade<\/code>\n  - Pin exact <code>==<\/code> versions only where no lockfile exists (standalone scripts, requirements.txt)\n- Use <code>uv<\/code> for project and environment management\n  - <code>uv run<\/code> instead of <code>python3<\/code> \u2014 picks up the project venv and dependencies automatically\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  - <code>assert<\/code> is fine in tests but use <code># noqa: S101 assert<\/code> elsewhere\n- When the linter flags something, read the rule it enforces (<code>ruff rule &lt;CODE&gt;<\/code>) and fix the code\n  - Suppress with <code># noqa<\/code> only when the rule does not apply to the project\n  - All <code># noqa<\/code> comments must include 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> if multiple rules\n- Use <code>ty<\/code> for type checking\n- Use <code>ty<\/code> LSP tool for code navigation when grep's text matching would be ambiguous\n- Use <code>TypedDict<\/code> for structured dicts \u2014 not plain dicts or dataclasses<\/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, something similar to <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: Caveman\ndescription: Terse caveman-speak replies \u2014 full technical substance, no filler\nkeep-coding-instructions: true\n---\n\nRespond terse like smart caveman. All technical substance stay. Only fluff die.\n\n## Rules\n\nDrop: articles (a\/an\/the), filler (just\/really\/basically\/actually\/simply), pleasantries (sure\/certainly\/happy to), hedging, decorative tables\/emoji. Fragments OK. Short synonyms (fix, not \"implement a solution for\"), one word when one word enough. Pattern: <code>[thing] [action] [reason]. [next step].<\/code>\n\nLead with answer. First sentence carry verdict or result; reason after, never before.\n\nState each fact once \u2014 never restate same fact in second form.\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\nNever drop not\/never\/no\/only\/except \u2014 flipped meaning worse than any token saved. Numbers, units, technical terms exact.\n\nCode blocks, commands, API names, error strings: byte-exact, never compressed. Long error log: quote shortest decisive line, not whole dump.\n\nToken economics: standard acronyms OK (DB\/API\/HTTP). Never invent abbreviations (cfg\/impl\/req\/res\/fn) \u2014 tokenizer split them same as full word, zero token saved, reader still decode. No causal arrows (\u2192) \u2014 own token, save nothing. Full word cheaper AND clearer.\n\nTool calls: fire direct, no progress narration before or between calls. CLAUDE.md duties survive compressed, never dropped: pre-change outline bullets, named-assumption bullets, findings user need \u2014 write them caveman-terse.\n\nReply in user's language \u2014 compress style, never translate. Drop articles only in languages that have them; where small markers carry case\/role (particles), keep them: grammar, not filler.\n\nNo self-reference. Never announce or name the style, no \"caveman mode on\". Caveman output only \u2014 never normal answer plus caveman recap.<\/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<ul>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/dotfiles\/.claude\/output-styles\/caveman.md\">GitHub: vinta\/hal-9000 - caveman.md<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/blob\/main\/dotfiles\/.claude\/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    \"CLAUDE_CODE_RETRY_WATCHDOG\": \"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\": \"opus[1m]\",\n  \"effortLevel\": \"high\",\n  \"advisorModel\": \"fable\",\n  \"cleanupPeriodDays\": 99999,\n  \"includeGitInstructions\": false,\n  \"showClearContextOnPlanAccept\": true,\n  \"teammateMode\": \"auto\",\n  \"voice\": { \"enabled\": true },\n  \"spellcheck\": { \"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;: 365<\/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<li><code>&quot;spellcheck&quot;: { &quot;enabled&quot;: true }<\/code>: Mark misspelled words in the prompt input using <code>aspell<\/code> or <code>hunspell<\/code><\/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      }\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 openai\/codex-plugin-cc\n\/plugin marketplace add xai-org\/grok-build-plugin-cc\n\/plugin marketplace add mattpocock\/skills\n\/plugin marketplace add vinta\/hal-9000\n\n# browse plugins\n\/plugin<\/code><\/pre>\n<p>Recommended:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/openai\/codex-plugin-cc\">GitHub: openai\/codex-plugin-cc<\/a>: OpenAI Codex's official plugin for Claude Code<\/li>\n<li><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<\/li>\n<li><a href=\"https:\/\/github.com\/obra\/superpowers\">GitHub: obra\/superpowers<\/a>: The first skills I ever installed and still find them useful<\/li>\n<li><a href=\"https:\/\/github.com\/mattpocock\/skills\">GitHub: mattpocock\/skills<\/a>: This is my new favorite<\/li>\n<li><a href=\"https:\/\/github.com\/vinta\/hal-9000\/tree\/main\/skills\">GitHub: vinta\/hal-9000<\/a>: My personal skills<\/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 best-practices brainstorm blindspot simple-english write-like-me refactor-agents-md \\\n--agent codex \\\n-g\n\n# workflow skills\nnpx skills add https:\/\/github.com\/mattpocock\/skills --agent codex -g\nnpx skills add https:\/\/github.com\/cursor\/plugins --skill thermo-nuclear-code-quality-review --agent codex claude-code -g\n\n# writing skills\nnpx skills add https:\/\/github.com\/cursor\/plugins --skill unslop\nnpx skills add https:\/\/github.com\/kotek-7\/dotfiles --skill sanitize-artifacts\nnpx skills add https:\/\/github.com\/shyuan\/writing-humanizer --skill writing-humanizer\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\/trailofbits\/skills-curated --skill python-code-simplifier\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\nnpx skills add https:\/\/github.com\/wdm0006\/python-skills\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\/supabase\/agent-skills\nnpx skills add https:\/\/github.com\/planetscale\/database-skills\n\n# LLM API skills\nnpx skills add https:\/\/github.com\/openai\/skills\nnpx skills add https:\/\/github.com\/google-gemini\/gemini-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\nnpx skills add https:\/\/github.com\/openai\/skills --skill frontend-skill\n\n# seo\/aeo skills\nnpx skills add https:\/\/github.com\/warpdotdev\/oz-skills --skill seo-aeo-audit\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>\/brainstorming<\/code> from <a href=\"https:\/\/github.com\/obra\/superpowers\">superpowers<\/a>: When in doubt, start with this skill<\/li>\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>\/frontend-design<\/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>\/simplify<\/code>: 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<h2>Context7 MCP<\/h2>\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\">npx ctx7 setup<\/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. Both tools support <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\nnpx skills add https:\/\/github.com\/microsoft\/playwright-cli<\/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<h3>Codex MCP<\/h3>\n<p>Yes, ironically. Other coding agents like Claude Code can use Codex via MCP, which is slightly more stable than directly invoking it with <code>codex exec<\/code> via CLI.<\/p>\n<pre class=\"line-numbers\"><code class=\"language-bash\"># Codex reads your local .codex\/config.toml by default\nclaude mcp add codex --scope user -- codex mcp-server<\/code><\/pre>\n<ul>\n<li><a href=\"https:\/\/developers.openai.com\/codex\/cli\/reference\/#codex-mcp-server\">Codex Command Line Options - codex mcp-server<\/a><\/li>\n<\/ul>\n<p>However, since OpenAI releases the official Claude Code plugin: <a href=\"https:\/\/github.com\/openai\/codex-plugin-cc\">codex-plugin-cc<\/a>, you should probably use that instead.<\/p>\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 ccfable=\"claude --model fable --effort max\"\nalias ccsonnet=\"claude --model sonnet --effort high\"\nalias ccyolo=\"claude --dangerously-skip-permissions\"\nccp() { claude --model sonnet --effort high --safe-mode --no-session-persistence --no-chrome -p \"$*\"; }<\/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>: skip CLAUDE.md\/skills\/plugins\/MCP\/auto-memory loading<\/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}]}}