Skip to content

AI Coding Instructions ​

Reusable coding instructions, agent skills, custom agents, and prompt templates for AI coding agents.

ai/
├── common/                       # shared — same files read by every tool
│   ├── instructions/*.instructions.md   # general + per-language/platform + per-feature, one copy each
│   └── skills/<name>/SKILL.md
├── copilot/
│   ├── agents/<name>.md          # Copilot-only — tool aliases (read, edit, search, execute, web)
│   └── config/mcp.json           # Copilot-only — VS Code workspace MCP template (${env:VAR} expansion)
└── claude/
    ├── agents/<name>.md          # Claude Code-only — real tool names, model/effort/skills fields
    ├── skills/<name>/SKILL.md    # Claude Code-only skills — not mirrored to Copilot
    └── config/                   # Claude Code-only — settings.json (permissions.deny) + mcp.json (${VAR} expansion)

Each instruction file exists once, in Copilot's format: an applyTo glob in the frontmatter scopes it to matching files. The installer derives every other form from that one copy: the frontmatter is dropped where the text is always on (AGENTS.md, ~/.claude/general-instructions.md, ~/.copilot/copilot-instructions.md), and applyTo becomes paths: in a Claude Code rule (.claude/rules/<name>.md), so a scoped file stays scoped for both tools. Only agents/ needs a real per-tool split, since Copilot and Claude Code name their tools differently.

Four kinds of content: instructions (always-on or file-scoped guidance), skills (workflows loaded only when relevant — avoids bloating context), agents (specialist personas with restricted tools), config (settings/MCP templates for scoping permissions and secrets — see Secrets & Permission Scoping).

NOTE

See the dotfiles repo for a real-world example of scripting this setup end-to-end (personal Claude/Copilot settings, MCP servers) as part of a full machine bootstrap.

Quick Start ​

One-liner — bootstraps instructions + skills for both tools at once:

sh
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/shell/setup-ai-agent.sh" | bash -s -- -t both

Use -t claude or -t copilot to set up just one, -p <preset> for a stack-specific instruction set (javascript, python, kubernetes, go), -i <names> for an explicit list, -a to also install agents, -P to also install Claude Code plugins at user scope, -g for personal/global instead of repo setup, or -r to safely run rtk init -g --copilot without losing existing content (see RTK below). -c is still accepted but no longer needed: with -g, Copilot always gets the general instructions in ~/.copilot/copilot-instructions.md. See setup-ai-agent.sh or run it with -h for details.

The script downloads everything it needs before writing anything, follows main by default (set TOOLS_REF=<tag or commit SHA> to pin what gets installed) and can install from a local checkout with RAW_BASE=file:///path/to/tools.

What goes where ​

Every piece lands once per tool, in a place that tool reads, so nothing is loaded twice when both tools are set up — including in VS Code, which reads Copilot's and Claude Code's folders alike (see How many times each piece is loaded).

PieceRepository (default)Personal (-g)
General instructionsAGENTS.md, in a marked block — every tool reads itClaude Code: ~/.claude/general-instructions.md, imported by ~/.claude/CLAUDE.md
Copilot: ~/.copilot/copilot-instructions.md, in a marked block
Scoped instructions (-p, -i)Claude Code: .claude/rules/<name>.md (paths:), also read by Copilot CLI and VS Code
Copilot only: .github/instructions/<name>.instructions.md
Copilot: ~/.copilot/instructions/<name>.instructions.md
Claude Code: none (general only)
Per-feature instructions (-i commit-message,pull-request,code-review)Copilot: .github/feature-instructions/<name>.md, read through VS Code settings only
Claude Code: the skills of the same name
not installed (VS Code can't read them from the home directory)
Skills.claude/skills/<name>/ (Claude Code, Copilot CLI and VS Code read it)
Copilot only: .github/skills/<name>/
~/.claude/skills/<name>/ and ~/.copilot/skills/<name>/ (Copilot CLI doesn't read ~/.claude/; VS Code keeps one skill per name)
Agents (-a).claude/agents/ and/or .github/agents/ (one format per tool)~/.claude/agents/ and/or ~/.copilot/agents/
  • Copilot uses Claude Code's copies when they exist. In a repository, a Copilot run skips a scoped file or skill already in .claude/rules or .claude/skills (and removes the .github/ copy an earlier run installed), since Copilot CLI (1.0.89 and later for rules) and VS Code read those folders too. GitHub.com's cloud agent and code review read only .github/instructions: in a repository without Claude Code rules, -t copilot installs the scoped files there; with both, copy a file there by hand if you need it on GitHub.com (it is then loaded twice locally).
  • AGENTS.md and CLAUDE.md. Claude Code reads AGENTS.md by itself (v2.1.277 and later) while there is no CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md. When a CLAUDE.md exists, the script adds an @AGENTS.md import to it once; on an older Claude Code, create one with echo '@AGENTS.md' > CLAUDE.md.
  • Marked blocks. The general instructions sit between <!-- general-instructions <checksum> --> and <!-- /general-instructions -->. Re-runs replace that block in place and leave everything else in the file (your own content, rtk's block) untouched.
  • systematic-debugging is not installed while the superpowers plugin is installed or enabled, since the plugin ships a skill with the same trigger (see Superpowers). A personal setup follows your plugins (-P, ~/.claude/settings.json); a repository follows its own .claude/settings.json only, so the result doesn't depend on who runs the script.
  • Agents need one file per tool, and VS Code's Local agent lists every folder: with both installed, "chat.agentFilesLocations": { ".claude/agents": false } (or "~/.claude/agents": false) in VS Code lists each agent once.

Backups, manifest and pruning ​

Each install root (.claude/, .github/, ~/.claude/, ~/.copilot/) gets a .setup-ai-agent-manifest file: one <checksum> <path> line per file the script installed there. On each run:

  • A file that differs from the new version is replaced silently when it is still exactly what the script installed, and kept as <name>.bak.<timestamp> first when you edited it (or when it predates the manifest but has the heading of the shipped file; a file with another heading is left alone with a warning). In a repository, a file git already holds without local changes needs no extra copy.
  • Skills (and agents, with -a) the script installed and no longer ships are removed, with the same backup rule. Instructions are only added, never pruned, except the copies the current layout moved (general.instructions.md, a CLAUDE.md written by older runs, per-feature files in an instructions folder, .github/ copies of what .claude/ now holds).
  • Files the script did not install are never replaced or removed: a file is the script's when the manifest lists it, or, for files from runs that predate the manifest, when its first heading is the one of the file shipped there. Otherwise the script warns and leaves it. In your own AGENTS.md, CLAUDE.md and copilot-instructions.md it only maintains its marked block or adds its import line.
  • Files are written through a temporary file renamed in place (mode kept), so an interrupted run never leaves a truncated file; the only in-place edits are your own CLAUDE.md import line and a symlinked target. Manifest entries that point outside the install root are ignored. Re-running is idempotent (no change, no new backup).

Commit the repository manifests along with the files, so a teammate's run knows what the script manages. The first run after upgrading from a version without manifests keeps a backup of each file it replaces.

Or do it by hand, one curl at a time:

Claude Code ​

sh
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/general.instructions.md" \
  -o "AGENTS.md"   # read by Claude Code (v2.1.277+) when there is no CLAUDE.md; otherwise add '@AGENTS.md' to it

for skill in code-review repository-audit commit-message pull-request; do
  mkdir -p ".claude/skills/${skill}"
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/skills/${skill}/SKILL.md" \
    -o ".claude/skills/${skill}/SKILL.md"
done

GitHub Copilot ​

sh
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/general.instructions.md" \
  -o "AGENTS.md"   # or .github/copilot-instructions.md for a Copilot-only repository

for skill in code-review repository-audit commit-message pull-request; do
  mkdir -p ".github/skills/${skill}"
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/skills/${skill}/SKILL.md" \
    -o ".github/skills/${skill}/SKILL.md"
done

That's a working baseline for either tool (the applyTo frontmatter left at the top of a hand-downloaded AGENTS.md is inert text). Keep one copy per piece when both tools share a repository: one AGENTS.md, skills in .claude/skills only (Copilot reads it). See the sections below for scoped per-language instructions, personal/global setup, custom agents, and every other option.


Claude Code ​

Repo setup ​

Claude Code reads CLAUDE.md, .claude/CLAUDE.md and CLAUDE.local.md at the repository root — or, when none of them exists, AGENTS.md (v2.1.277 and later) — plus the rules in .claude/rules/. Keep the general instructions in AGENTS.md, which every other tool reads too, and turn each scoped file into a rule: its applyTo glob becomes paths:, so the rule only loads when Claude reads or edits a matching file (a rule without paths: loads in every session).

sh
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/general.instructions.md" \
  -o "AGENTS.md"
# a CLAUDE.md that already exists has to import it: echo '@AGENTS.md' >> CLAUDE.md

# then one rule per scoped file relevant to this repo, e.g.:
mkdir -p .claude/rules
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/docker.instructions.md" \
  | awk 'NR == 2 && sub(/^applyTo: /, "") { print "paths:"; print "  - " $0; next } { print }' \
  > ".claude/rules/docker.md"

The setup script does the same (-t claude -p <preset> or -i <names>). Copilot CLI (1.0.89 and later) and VS Code read .claude/rules as well, so in a repository set up for both tools these rules are the only copy. Don't paste scoped files into CLAUDE.md: they would load in every session, whatever file Claude works on.

See Available Instructions for the full list of scoped files.

Personal instructions (user profile) ​

Claude Code always reads ~/.claude/CLAUDE.md for every repo — no settings/opt-in flag required. Since this script installs no personal rules, only tool-agnostic, always-relevant guidance belongs here — the General Development instructions fit, the language/platform-scoped ones don't.

Use CLAUDE.md's @path/to/file import syntax to compose it alongside other personal files rather than overwriting the whole thing:

sh
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/general.instructions.md" \
  -o "${HOME}/.claude/general-instructions.md"
grep -qxF "@general-instructions.md" "${HOME}/.claude/CLAUDE.md" || echo "@general-instructions.md" >> "${HOME}/.claude/CLAUDE.md"

The import also keeps this copy away from Copilot: VS Code reads ~/.claude/CLAUDE.md (with chat.useClaudeMdFile) but doesn't expand @ imports, so Copilot only gets its own copy from ~/.copilot/copilot-instructions.md. A personal rule in ~/.claude/rules/ would not have that property: VS Code reads that folder by default.

Skills ​

Claude Code reads skills from .claude/skills/ (repo) or ~/.claude/skills/ (personal, applies to every repo — no opt-in needed). The ai/common/skills/*/SKILL.md files are already in Claude's native format (no conversion needed):

sh
# Repo
for skill in code-review repository-audit commit-message pull-request; do
  mkdir -p ".claude/skills/${skill}"
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/skills/${skill}/SKILL.md" \
    -o ".claude/skills/${skill}/SKILL.md"
done

# Personal (swap the target dir)
for skill in code-review repository-audit commit-message pull-request; do
  mkdir -p "${HOME}/.claude/skills/${skill}"
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/skills/${skill}/SKILL.md" \
    -o "${HOME}/.claude/skills/${skill}/SKILL.md"
done
  • Automatic: Claude Code detects when a skill is relevant and loads it (based on description in frontmatter)
  • Explicit: Include the skill name with a / prefix in your prompt: /code-review analyze the auth module

The repository folder .claude/skills/ is also read by Copilot CLI and VS Code, so a repository set up for both tools needs no .github/skills/ copy. The personal folder is not: Copilot CLI stopped reading ~/.claude/ in 1.0.36, so a personal setup keeps a copy in ~/.copilot/skills/ too (VS Code, which reads both, keeps one skill per name).

Custom Agents ​

Claude Code subagents live in .claude/agents/<name>.md. The frontmatter differs from Copilot's agent format — tools: lists actual tool names instead of aliases. Claude Code delegates to a subagent based on its description, so each description says when to use the agent ("Use when ...", and "Use proactively" for the reviewers); you can also invoke one by name. Canonical source: ai/claude/agents/.

Some agents also tune model: and effort: for cost/quality — cheaper model: haiku for mechanical, low-risk work; effort: high (same model, more reasoning) for analysis where missing a real bug/vuln is costly. code-reviewer also preloads the code-review skill via skills: [code-review] instead of duplicating its checklist inline, so the two can't drift out of sync. These fields are Claude Code-only — Copilot's agent schema doesn't have model/effort/skills in the same shape, so ai/copilot/agents/ intentionally doesn't mirror them.

NameDescriptionToolsModel / Effort
Code ReviewerRead-only review of the change it is given (preloads code-review skill)Read, Grep, Globeffort: high
Security AuditorSecurity-focused vulnerability analysisRead, Grep, Globeffort: high
RefactorerImproves structure while preserving behavior, runs the tests after each stepRead, Edit, Write, Grep, Glob, Bashdefault
Test WriterGenerates tests following project conventions, and runs themRead, Edit, Write, Grep, Glob, Bashmodel: haiku
Doc WriterGenerates and updates documentation from codeRead, Edit, Write, Grep, Globmodel: haiku
Migration ResearcherCollects an upgrade's breaking changes from the web, with sourcesWebFetch, WebSearchdefault
Migration AssistantApplies upgrades and migrations, builds and tests after each changeRead, Edit, Write, Grep, Glob, Bashdefault
IaC ReviewerReviews Terraform, K8s, Docker, CI/CD configsRead, Grep, Globeffort: high

Why these tool sets:

  • Reviewers stay read-only, with no shell. A subagent's tools field takes tool names (a specifier such as Bash(git diff *) is not a documented form there, and in disallowedTools it removes the whole tool), and even git diff can write a file (--output) or run a program the repository configures (an external diff driver). So code-reviewer works from the diff, or the changed files and base, that the caller passes in the request, and says so when it gets neither.
  • Research and apply are two agents. An agent that both reads web pages and runs shell commands can be steered by a crafted changelog into running a command that reads the environment — where tokens exported by the shell live — and sends it somewhere. migration-researcher reads the web but has no file, shell or environment access; migration-assistant has the shell but no web access, works from the notes it is handed, and treats them as data. Run the researcher first, then hand its report to the assistant.
  • Writers that must verify get the shell. refactorer and test-writer run the project's own test, lint and build commands, and are told to use the shell for nothing else.
Copilot tool aliasClaude Code tool name(s)
readRead, Glob
searchGrep, Glob
editEdit, Write
executeBash
webWebFetch, WebSearch
agentTask
todoTodoWrite
sh
AGENTS="code-reviewer security-auditor refactorer test-writer doc-writer migration-researcher migration-assistant iac-reviewer"

# Repo
mkdir -p .claude/agents
for agent in $AGENTS; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/claude/agents/${agent}.md" \
    -o ".claude/agents/${agent}.md"
done

# Personal (swap the target dir) — applies to every repo, no opt-in needed
mkdir -p "${HOME}/.claude/agents"
for agent in $AGENTS; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/claude/agents/${agent}.md" \
    -o "${HOME}/.claude/agents/${agent}.md"
done

VS Code lists the agents of .claude/agents/ and ~/.claude/agents/ too (it maps Claude tool names to its own) and doesn't merge agents of the same name: with the Copilot versions installed as well, hide one folder from its Local agent ("chat.agentFilesLocations": { ".claude/agents": false }).

Claude-only Skills ​

Some skills are Claude Code-specific — either because they lean on Claude Code-only mechanisms, or because they're deliberately not worth mirroring to Copilot (e.g. they'd duplicate a capability Copilot already ships natively). These live in ai/claude/skills/ instead of ai/common/skills/, same SKILL.md format, just not distributed to ai/copilot/.

NameDescription
Systematic DebuggingReproduce → isolate → one hypothesis at a time, for bugs that resist a quick fix
sh
# Repo
mkdir -p .claude/skills/systematic-debugging
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/claude/skills/systematic-debugging/SKILL.md" \
  -o ".claude/skills/systematic-debugging/SKILL.md"

# Personal (swap the target dir) — applies to every repo, no opt-in needed
mkdir -p "${HOME}/.claude/skills/systematic-debugging"
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/claude/skills/systematic-debugging/SKILL.md" \
  -o "${HOME}/.claude/skills/systematic-debugging/SKILL.md"

Skip it when the superpowers plugin is installed: the plugin ships its own systematic-debugging with the same trigger, and the setup script leaves this one out in that case.

NOTE

Before reaching for a third-party skill pack (e.g. Superpowers-style marketplaces), check whether Claude Code already ships the capability natively — verify, simplify, security-review, loop, and schedule are built in as of this writing. A whole marketplace usually just duplicates those, so cherry-pick the specific workflow you're missing (ai/claude/skills/) instead of a blanket import — unless the marketplace itself clears the bar; see the note below.

The same bar applies to plugins, one level up — see Plugins for the ones that currently clear it.

Plugins ​

Plugins bundle skills, agents, hooks, and MCP servers into a single installable unit. Unlike everything else on this page they aren't files you curl into place — they're installed through Claude Code's own CLI from a marketplace, and registered in ~/.claude/settings.json (user scope) or .claude/settings.json (project scope). That makes them Claude Code-only, with no Copilot equivalent to mirror.

The bar from the note above still applies: prefer native capabilities, then a cherry-picked skill in ai/claude/skills/, and only reach for a plugin when it delivers something neither can. Two currently clear it.

Claude Security (claude-security) ​

First-party Anthropic plugin (official marketplace) that runs a multi-agent vulnerability scan inside your session: it maps the architecture, builds a threat model, hunts for vulnerabilities, then has independent verifier agents try to disprove every candidate before it reaches the report. Surviving findings can be turned into patches, each reviewed by an agent other than the one that wrote it.

It's the deep-scan layer, and it complements rather than replaces what's already here — see How it relates to the security-auditor agent below.

sh
# Via the setup script (user scope — available in every repo)
./setup-ai-agent.sh -t claude -g -P

# Or by hand
claude plugin marketplace add anthropics/claude-plugins-official
claude plugin install claude-security@claude-plugins-official -s user

Then /reload-plugins to activate without restarting the session.

Requirements: Claude Code ≥ 2.1.154 on a paid plan (the scan runs on dynamic workflows — on Pro, enable them in /config), python3 ≥ 3.9.6 on PATH (stdlib only, nothing gets installed), and git for change scans and patches. A full scan works in any directory, versioned or not.

Cost: claude plugin details claude-security@claude-plugins-official reports ~642 tokens always-on — 7 agents plus 1 skill, all lazily loaded, and the single hook is UserPromptExpansion display-only (harness-only, no model context cost). Nothing else runs until you invoke it. The scan itself is the expensive part and is opt-in per run.

Usage — /claude-security opens a menu of three jobs, or ask directly:

InvocationWhat it does
/claude-securityMenu: scan codebase, scan changes, suggest patches
/claude-security scan my branchScans only the branch's diff against its base
"scan commit abc1234"Scans a single commit
"fix finding F3"Turns one report finding into a reviewed patch
Only committed changes are scanned — commit or stash first, or run a full scan (which reads the working tree). Results land in CLAUDE-SECURITY-<timestamp>/: the report, a .jsonl of the same findings, and a revision stamp tying them to the exact commit (suffixed -dirty when uncommitted changes were in the scanned tree). That directory ships its own .gitignore, so no repo-level ignore rule is needed and a stray git add can't sweep it into a commit. Patches are never auto-applied:
sh
git apply CLAUDE-SECURITY-<timestamp>/patches/F1.patch

Scans are nondeterministic — two runs over the same code can surface different findings. Scope tightly on large repos (one area at a time; the report's coverage section states what was and wasn't examined), especially on a high-effort/Opus configuration where a full-tree scan fans out across all 7 agent types.

NOTE

Plugin state lives in ~/.claude/settings.json as enabledPlugins + extraKnownMarketplaces. Anything that overwrites that file wholesale — a dotfiles cp, a config restore — silently drops the plugin. Either re-run the install afterwards, or declare both keys in the file you deploy so it carries the registration itself (which is what the dotfiles repo does).

How it relates to the security-auditor agent ​

They overlap on subject matter and nothing else — no conflict, and the redundancy is worth keeping. security-auditor is a single read-only agent (Read/Grep/Glob, effort: high) working a fixed 7-step checklist over OWASP Top 10, secrets, dependencies, and infra configs, and reporting straight into the conversation. claude-security is an orchestrated fleet — inventory, researcher, and verifier agents plus a patch generator and patch verifier — that builds a threat model first and discards any finding its verifiers can't confirm.

That difference makes them good at different things:

Aspectsecurity-auditor agentclaude-security plugin
ShapeOne agent, one pass7 agents, workflow-orchestrated
MethodFixed checklistThreat model → hunt → adversarial verify
Speed / costSeconds, cheapMinutes, significant
False positivesReported as foundDiscarded unless a verifier confirms
OutputMarkdown in-conversationReport + .jsonl + revision stamp on disk
FixesRecommendsGenerates reviewed .patch files
Infra coverageDocker, K8s RBAC, CI/CD permsApplication code first
Use the agent for a quick targeted pass ("audit this module", "check these K8s manifests"), the plugin for a real audit of a repo or a release diff. The agent's IaC/CI focus is also broader than the plugin's — for a repo like this one — mostly shell, Dockerfiles, and CI workflows — security-auditor and iac-reviewer stay the more relevant tools.

Deliberately not installed: security-guidance ​

The official docs pair claude-security with security-guidance@claude-plugins-official, which hooks Stop and PostToolUse to review Claude's own diffs continuously. It's a good plugin with very different economics: it fires a model review (Opus 4.7 by default) after every turn that changes files, plus a deeper agentic review on every commit and push Claude makes — capped at 20 per rolling hour. That's a standing cost on every session rather than an opt-in one, so it's left out of the -P default set. Enable it per-repo where it earns its keep:

json
{
  "enabledPlugins": {
    "security-guidance@claude-plugins-official": true
  }
}

Its two extension points are plain files and work regardless of how it was enabled — .claude/claude-security-guidance.md (repo threat model, feeds the model-backed reviews) and .claude/security-patterns.yaml (deterministic per-edit regex/substring rules, no model call). The pattern file is a useful complement to the permissions.deny rules in Secrets & Permission Scoping: denies gate Read/Edit only, whereas a pattern rule fires on content written to any file, catching things like an sk_live_/AKIA prefix landing in a commit. Set SECURITY_REVIEW_MODEL / SG_AGENTIC_MODEL to run the reviews on a cheaper model, or SECURITY_GUIDANCE_DISABLE=1 to switch it off without uninstalling.

Where each security layer fits ​

StageToolCost model
As Claude writessecurity-guidance pluginPer turn + per commit, automatic
On demand, single passnative /security-reviewPer invocation, branch diff only
On demand, deep scanclaude-security pluginPer invocation, whole repo or diff
Read-only analysissecurity-auditor agentPer invocation, no writes
In CIYour existing SAST / dependency scannersDeterministic, every push
None replaces the others, and none replaces conventional static analysis — they reason about code the way a reviewer would, which is a different failure mode from rule-based scanners.

Superpowers (superpowers) ​

Jesse Vincent's skills library (MIT), distributed through the same official marketplace as claude-security — no separate marketplace registration needed. 14 skills, most covering ground nothing above does: brainstorming, test-driven-development, writing-plans / executing-plans, dispatching-parallel-agents / subagent-driven-development, requesting-code-review / receiving-code-review, finishing-a-development-branch, using-git-worktrees, and writing-skills (for extending the set itself) — plus using-superpowers as a live index and verification-before-completion.

sh
# Via the setup script (user scope — available in every repo)
./setup-ai-agent.sh -t claude -g -P

# Or by hand
claude plugin install superpowers@claude-plugins-official -s user

Then /reload-plugins to activate without restarting the session.

Cost: claude plugin details superpowers@claude-plugins-official reports ~688 tokens always-on for the 14 skills plus one harness-only SessionStart hook (0 agents, 0 MCP servers) — each skill's own content only loads when it actually fires. No special requirements, unlike claude-security (no paid-plan or python3 dependency).

Usage: skills load automatically when Claude Code judges them relevant, same discovery mechanism as every other skill on this page — or ask for one by name.

Overlap with systematic-debugging: the plugin bundles a skill of the same name as the one cherry-picked into ai/claude/skills/ above. Plugin skills are namespaced (superpowers:systematic-debugging), so the two would both load, with the same trigger, and Claude would be offered two competing debugging workflows. The setup script therefore doesn't install its own copy while superpowers is installed (-P) or enabled in ~/.claude/settings.json (for a repository: enabled in its .claude/settings.json), and removes the one an earlier run installed (kept as SKILL.md.bak.<timestamp> if you edited it). Without the plugin, the cherry-picked skill is installed as before.


GitHub Copilot ​

VS Code ​

How it works ​

VS Code runs chat sessions in two kinds of harness. The Local agent finds customization files through the defaults and the chat.* settings below; Agent Host sessions use the discovery rules of the selected harness (Copilot CLI's for Copilot, Claude Code's for Claude — see Discovery order by tool). What the Local agent reads by default:

File/FolderPurpose
AGENTS.mdAlways on (chat.useAgentsMdFile, on by default)
.github/copilot-instructions.md, ~/.copilot/copilot-instructions.mdAlways on (github.copilot.chat.codeGeneration.useInstructionFiles, on by default)
CLAUDE.md, .claude/CLAUDE.md, CLAUDE.local.md, ~/.claude/CLAUDE.mdAlways on with chat.useClaudeMdFile — @ imports are not expanded, the file is sent as is
.github/instructions/*.instructions.md, ~/.copilot/instructions/Scoped by applyTo; a file without applyTo is only used when attached by hand
.claude/rules/*.md, ~/.claude/rules/Scoped by paths: (none means every file)
.github/skills/, .claude/skills/, .agents/skills/ and ~/.copilot/skills/, ~/.claude/skills/, ~/.agents/skills/Agent skills, on demand — one skill per name (a workspace skill wins over a personal one)
.github/agents/, .claude/agents/, ~/.copilot/agents/, ~/.claude/agents/Custom agents — every folder is listed, agents of the same name are not merged
.github/prompts/*.prompt.mdPrompt files — reusable templates

Instruction sources are additive: the same text in two of these places is sent twice. That's why the setup script puts each piece in one place (see What goes where) — and why it never installs the general instructions in an instructions folder next to AGENTS.md or copilot-instructions.md.

Setup ​

Option 1 — General instructions only (quickest)

sh
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/general.instructions.md" \
  -o "AGENTS.md"

AGENTS.md is read by Copilot in VS Code, on the CLI and on GitHub, and by Claude Code; .github/copilot-instructions.md works too for a Copilot-only repository. Use one of them, not both. The applyTo frontmatter at the top is inert text in either file.

Option 2 — Scoped instructions (recommended for multi-tech repos)

sh
mkdir -p .github/instructions
INSTRUCTIONS="javascript docker kubernetes github-actions"
for name in $INSTRUCTIONS; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/${name}.instructions.md" \
    -o ".github/instructions/${name}.instructions.md"
done

In a repository also set up for Claude Code, use the rules in .claude/rules/ instead (see Claude Code — Repo setup): Copilot CLI and VS Code read them, and a second copy in .github/instructions/ would be loaded twice. GitHub.com's cloud agent and code review only read .github/instructions/.

Option 3 — Full setup (instructions + skills + agents)

sh
# Instructions
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/general.instructions.md" \
  -o "AGENTS.md"
mkdir -p .github/instructions
for name in javascript docker kubernetes github-actions; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/${name}.instructions.md" \
    -o ".github/instructions/${name}.instructions.md"
done

# Skills
for skill in code-review repository-audit commit-message pull-request; do
  mkdir -p ".github/skills/${skill}"
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/skills/${skill}/SKILL.md" \
    -o ".github/skills/${skill}/SKILL.md"
done

# Agents
mkdir -p .github/agents
for agent in code-reviewer security-auditor; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/copilot/agents/${agent}.md" \
    -o ".github/agents/${agent}.md"
done

See Recommended setup by project type for stack-specific presets.

Personal instructions (user profile) ​

~/.copilot/copilot-instructions.md is Copilot's one always-on personal file: VS Code (both harnesses) and Copilot CLI read it in every session, so the general instructions go there. The setup script keeps them in a marked block and leaves the rest of the file alone:

sh
./setup-ai-agent.sh -t copilot -g

Scoped personal files (-p/-i) go in ~/.copilot/instructions/, which VS Code and Copilot CLI also read by default — keep the general instructions out of it, or they are loaded twice. Older VS Code releases need that folder listed in chat.instructionsFilesLocations:

json
{
  "chat.instructionsFilesLocations": {
    ".github/instructions": true,
    "~/.copilot/instructions": true
  }
}

Configure via settings.json ​

All customization features can be enabled and relocated via settings. The workspace .vscode/settings.json applies to the project; your user settings.json applies globally. The *Locations settings are deprecated and only read by the Local agent; each default folder is on unless set to false.

jsonc
{
  // ── Instructions ─────────────────────────────────────────────────────────
  // Where to look for *.instructions.md / rule files (supports ~ expansion)
  "chat.instructionsFilesLocations": {
    ".github/instructions": true, // workspace (default on)
    ".claude/rules": true, // workspace, Claude format (default on)
    "~/.copilot/instructions": true, // personal (default on)
    "~/.claude/rules": true // personal, Claude format (default on)
  },
  "chat.includeApplyingInstructions": true, // auto-include files with matching applyTo
  "chat.useAgentsMdFile": true, // AGENTS.md always-on
  "chat.useClaudeMdFile": true, // CLAUDE.md always-on (its @ imports are not expanded)

  // ── Skills ───────────────────────────────────────────────────────────────
  "chat.useAgentSkills": true,
  "chat.agentSkillsLocations": {
    ".github/skills": true, // workspace
    ".claude/skills": true, // workspace, Claude alias
    "~/.copilot/skills": true, // personal
    "~/.claude/skills": true // personal, Claude alias
  },

  // ── Agents ───────────────────────────────────────────────────────────────
  "chat.agentFilesLocations": {
    ".github/agents": true, // workspace
    "~/.copilot/agents": true, // personal
    "~/.claude/agents": false // hide the Claude copies when both tools' agents are installed
  },

  // ── Prompts ──────────────────────────────────────────────────────────────
  "chat.promptFilesLocations": {
    ".github/prompts": true // workspace (default on)
  },

  // ── Per-feature instructions (commit, PR, review) ────────────────────────
  // `file` is resolved inside each workspace folder (no ~, no absolute path)
  "github.copilot.chat.commitMessageGeneration.instructions": [
    { "file": ".github/feature-instructions/commit-message.md" }
  ],
  "github.copilot.chat.pullRequestDescriptionGeneration.instructions": [
    { "file": ".github/feature-instructions/pull-request.md" }
  ],
  "github.copilot.chat.reviewSelection.instructions": [
    { "file": ".github/feature-instructions/code-review.md" }
  ],

  // ── Monorepo ─────────────────────────────────────────────────────────────
  // Discover instructions/skills/agents from parent folder when opening a subfolder
  "chat.useCustomizationsInParentRepositories": false
}

Per-feature instructions (commit, PR, review) ​

Three Copilot features read their own instructions from a setting — commit message generation, pull request descriptions, and review of a selection — and nothing else does. commit-message, pull-request and code-review are written for them, so they carry no applyTo: placed in an instructions folder they would otherwise be loaded into every chat (the pull request one alone is ~580 lines).

Each setting takes a list of { "file": "<path>" } or { "text": "<instruction>" } entries. A file is resolved inside each workspace folder: ~ and absolute paths don't work, so the file has to live in the repository, outside the instructions folders. The setup script installs them in .github/feature-instructions/ when they're named with -i (repository setup only, frontmatter removed):

sh
./setup-ai-agent.sh -t copilot -i general,commit-message,pull-request,code-review

# or by hand
mkdir -p .github/feature-instructions
for name in commit-message pull-request code-review; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/${name}.instructions.md" \
    -o ".github/feature-instructions/${name}.md"
done

Then point the settings at them (user or workspace settings.json, as in the example above). A repository without the file is skipped silently, so for a personal default in every repository add a short text entry next to the file one:

jsonc
"github.copilot.chat.commitMessageGeneration.instructions": [
  { "file": ".github/feature-instructions/commit-message.md" },
  { "text": "Conventional Commits: type(scope): subject, imperative, lowercase, no period, at most 72 characters; the body says why. Never mention AI tools or add Co-authored-by trailers for them." }
]

CLI ​

Copilot CLI reads from both the repository and your home directory. Personal config in ~/.copilot/ is loaded every session across all repos.

FileScope
~/.copilot/copilot-instructions.mdPersonal, always on
~/.copilot/instructions/**/*.instructions.mdPersonal, included when applyTo matches a file the CLI works with
~/.copilot/skills/<name>/SKILL.md, ~/.agents/skills/<name>/SKILL.mdPersonal skills (~/.claude/ is not read since 1.0.36)
~/.copilot/agents/<name>.mdPersonal agents
.github/copilot-instructions.md, AGENTS.md, CLAUDE.md, .claude/CLAUDE.md, GEMINI.mdRepository, always on (@ imports expanded in AGENTS.md/CLAUDE.md; identical files sent once)
.github/instructions/**/*.instructions.md, .claude/rules/*.md (1.0.89+)Repository, scoped
.github/skills/, .claude/skills/, .agents/skills/Repository skills
.github/agents/, .claude/agents/Repository agents

Personal setup (applies to all repos) ​

sh
# General instructions in ~/.copilot/copilot-instructions.md (marked block) + personal skills
./setup-ai-agent.sh -t copilot -g

# or by hand: skills — available everywhere
for skill in code-review repository-audit commit-message pull-request; do
  mkdir -p "${HOME}/.copilot/skills/${skill}"
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/skills/${skill}/SKILL.md" \
    -o "${HOME}/.copilot/skills/${skill}/SKILL.md"
done

Key CLI commands ​

CommandPurpose
/skills listList available skills
/skills infoDetail about a skill
/skills reloadReload after adding skills mid-session
/planCreate an implementation plan before coding
/reviewReview current changes
/delegateOffload work to cloud agent
/modelSwitch model mid-session
/contextShow context window usage
/fleetBreak task into parallel subtasks
/clear or /newReset context between unrelated tasks

Allowed tools ​

Pre-configure tool permissions via CLI flags:

sh
copilot --allow-tool='shell(git:*)' --deny-tool='shell(git push)'

Common patterns: shell(git:*), shell(npm run:*), shell(bun:*), write.

Agents ​

Agents define specialist personas with their own instructions and restricted tool access. Canonical source: ai/copilot/agents/. The tool sets follow the same reasoning as the Claude Code agents: reviewers read-only, web research split from shell access, writers that verify can run the tests.

NameDescriptionTools
Code ReviewerRead-only review of the change it is givenread, search
Security AuditorSecurity-focused vulnerability analysisread, search
RefactorerImproves structure, runs the tests after each stepedit, search, read, execute
Test WriterGenerates tests following project conventionsedit, search, read, execute
Doc WriterGenerates and updates documentation from codeedit, search, read
Migration ResearcherCollects an upgrade's breaking changes from the webweb
Migration AssistantApplies upgrades and migrations, no web accessedit, search, read, execute
IaC ReviewerReviews Terraform, K8s, Docker, CI/CD configsread, search

Setup (repository)

sh
mkdir -p .github/agents
for agent in code-reviewer security-auditor refactorer test-writer doc-writer migration-researcher migration-assistant iac-reviewer; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/copilot/agents/${agent}.md" \
    -o ".github/agents/${agent}.md"
done

Setup (personal)

Personal agents in ~/.copilot/agents/ are read by Copilot CLI and, by default, by VS Code (older releases need the folder in chat.agentFilesLocations).

sh
mkdir -p ~/.copilot/agents
for agent in code-reviewer security-auditor refactorer test-writer doc-writer migration-researcher migration-assistant iac-reviewer; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/copilot/agents/${agent}.md" \
    -o "${HOME}/.copilot/agents/${agent}.md"
done

Using agents

  • VS Code: Select the agent from the agent dropdown in the Chat panel, or let another agent call it as a subagent
  • Copilot CLI: Copilot auto-delegates to an agent when it detects a matching task, from its description (none of these agents sets disable-model-invocation, which would require selecting it by hand)
  • Cloud agent: Agents are available when assigned to a task on GitHub

Agent configuration

Agent files support these YAML frontmatter properties:

yaml
---
description: What the agent does and when to use it (required)
tools: [read, search] # Restrict available tools (omit for all)
model: claude-sonnet-4-5 # Override model (optional)
disable-model-invocation: false # If true, never called as a subagent
user-invocable: true # If false, hidden from the agent picker
---

Tool aliases: read, edit, search, execute (shell), web, agent, todo.


Reference ​

Available Instructions ​

All of these live in ai/common/instructions/ — pick the ones relevant to what you're setting up (General Development always applies; add scoped ones for the languages/platforms actually in the repo). The setup script places each one where it belongs (see What goes where).

NameDescriptionInstruction NameType
General DevelopmentHow to work, code, security and git rulesgeneralGeneral
Code ReviewCode review guidelines (lightweight)code-reviewPer-feature
Commit MessageConventional Commits formatcommit-messagePer-feature
Pull RequestPR description best practicespull-requestPer-feature
JavaScript/TypeScriptScoped to JS/TS files (incl. Bun)javascriptLanguage
GoScoped to Go filesgoLanguage
Bash/ShellScoped to shell scriptsshellLanguage
PythonScoped to Python files (FastAPI)pythonLanguage
TypeScript MonorepoComplete TS monorepo setupts-monorepoArchitecture
DockerScoped to DockerfilesdockerPlatform
Kubernetes/HelmScoped to K8s YAML fileskubernetesPlatform
GitHub ActionsScoped to workflow filesgithub-actionsCI/CD
Terraform/IaCScoped to Terraform filesterraformPlatform

Per-feature files have no applyTo: they are read through VS Code's per-feature settings, never loaded into a chat by themselves. Claude Code covers the same ground with the skills of the same name.

Available Skills ​

Skills are folders of instructions loaded on demand when the agent detects they're relevant to a task. All live in ai/common/skills/ — same format works for Copilot and Claude Code.

NameDescriptionSkill Name
Code ReviewFull review methodology with language checks & decision frameworkcode-review
Repository AuditComprehensive repo analysis (quality, security, performance, DevOps)repository-audit
Commit MessageWrites a Conventional Commits message (doesn't stage or commit)commit-message
Pull RequestPR description generationpull-request

commit-message and repository-audit also carry Claude Code-only frontmatter: model: haiku (commit-message) runs that formulaic turn on a cheaper, faster model, and effort: high (repository-audit) asks for more thorough reasoning on a task that's meant to be exhaustive. VS Code doesn't recognize these fields and ignores them, so the shared file stays a no-op-safe superset rather than forking per tool.

None of the skills pre-approves tools (allowed-tools). Claude Code already runs read-only forms of git (git status, git diff, git log) without a prompt, while a rule such as Bash(git diff *) would also approve write-capable forms like git diff --output=<file>. Staging and committing (once granted to commit-message) and writing files anywhere (once granted to repository-audit, which only needs to write REVIEW.md) go through the normal permission prompt: a skill that writes a message or a report has no business committing or writing elsewhere unasked.

See the Claude Code and Copilot sections above for setup commands and for the available custom agents (agent tables live there since the frontmatter schema is tool-specific). Claude-only skills (not mirrored to Copilot) are listed separately under Claude-only Skills.

Discovery order by tool ​

Each tool has its own discovery model. Repository-level settings take precedence over personal ones, but instructions from every source are combined, not overridden.

ToolRepo instructionsSkillsPersonal instructions
Claude CodeCLAUDE.md, .claude/CLAUDE.md, CLAUDE.local.md — or AGENTS.md when none exists (v2.1.277+); .claude/rules/ (paths:).claude/skills/, ~/.claude/skills/, plugins (plugin:skill)~/.claude/CLAUDE.md (+ @ imports), ~/.claude/rules/
VS Code (Local agent)AGENTS.md, .github/copilot-instructions.md, CLAUDE.md*, .github/instructions/, .claude/rules/.github/skills/, .claude/skills/, .agents/skills/, ~/.copilot/skills/, ~/.claude/skills/, ~/.agents/skills/ (one per name)~/.copilot/copilot-instructions.md, ~/.copilot/instructions/, ~/.claude/rules/, ~/.claude/CLAUDE.md*
Copilot CLI (and VS Code's Copilot Agent Host).github/copilot-instructions.md, AGENTS.md, CLAUDE.md, .claude/CLAUDE.md, .github/instructions/, .claude/rules/ (1.0.89+).github/skills/, .claude/skills/, .agents/skills/, ~/.copilot/skills/, ~/.agents/skills/~/.copilot/copilot-instructions.md, ~/.copilot/instructions/
GitHub cloud agent.github/copilot-instructions.md, .github/instructions/, AGENTS.md——
OpenAI CodexAGENTS.md.agents/skills/—

* With chat.useClaudeMdFile; VS Code sends the file as is, without expanding its @ imports.

How many times each piece is loaded ​

With the dotfiles setup (-t claude -g -P then -t copilot -g -c, and VS Code with chat.useClaudeMdFile and the personal skill folders enabled), before and after this layout:

PieceWhere it is readBeforeAfter
General instructionsClaude Code1 (~/.claude/general-instructions.md through the import)1
General instructionsVS Code Local agent2 (~/.copilot/instructions/general.instructions.md, applyTo: "**", and the block in ~/.copilot/copilot-instructions.md; ~/.claude/CLAUDE.md is sent too, but only its @ lines)1
General instructionsCopilot CLI / VS Code Copilot Agent Host2 (the block, plus the instructions file as soon as a file is involved)1
4 shared skillsClaude Code / Copilot CLI / VS Code1 each (two copies on disk, VS Code keeps one per name)1 each, unchanged
systematic-debuggingClaude Code2 (systematic-debugging and superpowers:systematic-debugging, same trigger)1 (the plugin's)
Commit / PR / review instructionsVS Code per-feature settings0 (the settings point at ~/.copilot/instructions/…: never installed, and ~ is not expanded there)1 with the proposed settings

And in a repository set up with -t both -p javascript:

PieceWhere it is readBeforeAfter
General instructionsClaude Code1 (CLAUDE.md)1 (AGENTS.md)
General instructionsVS Code Local agent2 (CLAUDE.md and .github/instructions/general.instructions.md)1 (AGENTS.md)
General instructionsCopilot CLI2 (same files)1 (AGENTS.md)
Scoped (javascript, docker, github-actions)Claude Codein every session, unscoped (inlined in CLAUDE.md)only for matching files (.claude/rules)
ScopedVS Code / Copilot CLIin every session (CLAUDE.md) plus once more for matching files (.github/instructions)only for matching files, once (.claude/rules)
4 shared skillsCopilot CLIdiscovered twice (.github/skills and .claude/skills)once (.claude/skills)
Agents (-a)VS Code Local agentlisted twice (.github/agents, .claude/agents)twice unless "chat.agentFilesLocations": { ".claude/agents": false } — one file per tool is required

Token-Efficient Terminal Commands (RTK) ​

rtk is a CLI proxy that filters/compresses terminal command output (git, tests, build/lint, containers, GitHub CLI, AWS, IaC, package managers, 100+ commands total) before it reaches the model context, cutting token usage by 60-90%.

Install the auto-rewrite hook — this is the only setup that guarantees every supported command gets rewritten, since it intercepts the shell tool call itself instead of relying on the model to remember to type rtk:

sh
# Install (macOS/Linux)
brew install rtk
# or: curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh

# Enable the hook for your tool
rtk init -g --copilot   # GitHub Copilot (VS Code + CLI)
rtk init -g             # Claude Code (default agent)

None of the instruction files in ai/common/instructions/ embed RTK guidance as prompt text — the hook is the only mechanism relied on.

If a Claude Code settings file you deploy carries the hook itself ("command": "rtk hook claude" under PreToolUse), every Bash call on a machine without rtk logs a hook error. Guard it so it does nothing there: "command": "command -v rtk >/dev/null 2>&1 || exit 0; exec rtk hook claude". rtk init -g writes its own, unguarded entry with the plain command, so keep only one of the two, or the rewrite runs twice. The settings template in this repository (ai/claude/config/settings.json) has no hook.

NOTE

The auto-rewrite hook only intercepts shell/terminal tool calls. Built-in editor tools (e.g. VS Code's file read/search tools) are never rewritten by the hook — they're a separate, already token-efficient code path.

WARNING

rtk init -g --copilot writes its block into ~/.copilot/copilot-instructions.md, where the setup script also keeps the general instructions. rtk 0.41.0 and later replace only their own block; older releases overwrite the whole file, wiping your personal instructions. With an older rtk (or to be safe), use setup-ai-agent.sh's -r flag instead: it saves the file as copilot-instructions.md.bak.<timestamp> (removed again when nothing changed), runs rtk, then writes your content back followed by the new rtk block, through a temporary file renamed in place. Any rtk block version (<!-- rtk-instructions vN -->) is recognized, so re-running never duplicates it, and when rtk fails the backup is kept and named:

sh
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/shell/setup-ai-agent.sh" | bash -s -- -r

rtk init -g (Claude Code, no --copilot flag) doesn't have this problem — it writes to its own separate file and is imported via @RTK.md, not baked into CLAUDE.md directly.

Sanity-checking hook coverage — run rtk discover occasionally; it scans recent session history for commands rtk already supports but that weren't rewritten (rtk gain shows the reverse: what's already been saved). Two things it reports are not live problems: commands from sessions that predate the hook being installed, and the handful of commands rtk doesn't support yet at all (check its unhandled-commands list against rtk-ai/rtk issues before assuming the hook itself is broken).

Secrets & Permission Scoping ​

Neither tool lets you allowlist which ambient shell env vars get inherited by the agent — that can only be enforced at the OS/process level (a wrapper script, a container, or per-project loaders like direnv). What both tools do support natively:

1. Never hardcode secrets in MCP config — reference them instead.

Both tools expand env vars inline, just with different syntax:

ToolSyntaxWhere it works
Claude Code${VAR} / ${VAR:-default}command, args, env, url, headers in .mcp.json / ~/.claude.json
Copilot (VS Code)${env:VAR}Same fields, in .vscode/mcp.json / user mcp.json

If the var is unset and there's no default, the literal ${VAR} is left in place and the tool warns at startup — so a missing secret fails loudly instead of silently sending an empty string. Templates: ai/claude/config/mcp.json, ai/copilot/config/mcp.json — both hold only the Context7 HTTP server, whose key comes from the environment.

sh
# Claude Code — project-scoped MCP config (git-safe as long as it only ever contains ${VAR} references, never literal values)
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/claude/config/mcp.json" -o ".mcp.json"

# Copilot — VS Code workspace MCP config
mkdir -p .vscode
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/copilot/config/mcp.json" -o ".vscode/mcp.json"

A local (stdio) server is a command that runs on every start with the secrets you give it, so pin it to an exact version and update it on purpose (for example through Renovate) — npx -y <package>@latest runs whatever was published last. The shape, with a placeholder package that doesn't exist:

jsonc
// .mcp.json (Claude Code) — "your-org/your-mcp-server" is a placeholder
{
  "mcpServers": {
    "your-server": {
      "command": "npx",
      "args": ["-y", "@your-org/your-mcp-server@1.2.3"],
      "env": { "YOUR_SERVER_TOKEN": "${YOUR_SERVER_TOKEN}" }
    }
  }
}

In VS Code's mcp.json the same entry goes under servers, with "type": "stdio" and ${env:YOUR_SERVER_TOKEN}.

NOTE

Claude Code's MCP scopes: project (.mcp.json, committed, shared) is for config only — keep it to ${VAR} references. Local and user scope (both inside ~/.claude.json, never committed) are where an actual secret value belongs if you'd rather set it directly than export it from the shell. Mixing the two up is how a real token ends up committed.

2. Deny reads/edits of secret files, regardless of what's in the prompt.

permissions.deny in .claude/settings.json blocks the Read/Edit tools from ever touching matching paths — it's enforced by the harness, not by the model remembering not to look. Template: ai/claude/config/settings.json.

sh
mkdir -p .claude
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/claude/config/settings.json" -o ".claude/settings.json"
jsonc
{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(**/.env.*)",
      "Read(**/*.pem)",
      "Read(**/*.key)",
      "Read(**/secrets/**)",
      "Edit(.env)",
      "Edit(**/.env.*)",
      "Edit(**/*.pem)",
      "Edit(**/*.key)",
      "Edit(**/secrets/**)"
    ]
  }
}

WARNING

Read/Edit denies only gate those two tools. They do not stop the Bash tool from running cat .env — Bash permission rules match command prefixes (e.g. Bash(git push:*)), not arbitrary file content, so there's no generic blanket block for shell access to a secret file. If a repo's Bash access needs to be untrusted around secrets, the real boundary is not putting the secret in a file/env var the agent's shell can reach in the first place (see direnv note below), not a permission rule.

Copilot/VS Code has no equivalent to permissions.deny today — the closest is not exporting secrets into the ambient environment a Copilot session inherits, plus ordinary .gitignore hygiene.

3. Prefer .claude/settings.local.json (gitignored) over .claude/settings.json (committed) for anything personal — e.g. a locally-relaxed permission or a machine-specific env value. Team-wide defaults like the deny rules above belong in the committed file; anything you wouldn't want a teammate to inherit belongs in the local one.

4. Scope ambient secrets per-project with direnv, instead of exporting everything to every shell. A single shell-profile file that exports every secret you own (common pattern: one env.sh sourced from .zshrc) means every shell — including any agent's Bash tool — inherits all of them, regardless of what the current project actually needs. direnv scopes exports to the current directory instead, loading them on cd in and unloading them on cd out. Keep each project's secrets in a git-ignored .env.local and load it from the project's .envrc with direnv's built-in dotenv_if_exists — no helper script and no extra tool:

sh
# .envrc — committed: says where the secrets come from, never their values
dotenv_if_exists .env.local

# .gitignore
.env.local
sh
# .env.local — git-ignored, one KEY=value per line, only what this project needs
ARGOCD_API_TOKEN=...
CONTEXT7_API_KEY=...

direnv allow (required once per directory before it'll load) is itself a safety feature — a .envrc can't silently start exporting things just because you cd'd into a repo you don't fully trust yet. An .envrc is a plain shell script, so if you keep your secrets in a manager, export TOKEN="$(your-tool get token)" there works too, with nothing for this repo to maintain.

NOTE

This scopes ambient exposure — what ends up in env, what an agent might echo/log by accident — it does not stop an agent with Bash access from reading .env.local directly. It's a least-privilege default, not a hard boundary against a deliberately-instructed agent; pair it with the permissions.deny rules above (they cover .env.*), not instead of them.

JavaScript / TypeScript

sh
mkdir -p .github/instructions
for name in javascript docker github-actions; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/${name}.instructions.md" \
    -o ".github/instructions/${name}.instructions.md"
done

Python (FastAPI)

sh
mkdir -p .github/instructions
for name in python docker github-actions; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/${name}.instructions.md" \
    -o ".github/instructions/${name}.instructions.md"
done

Kubernetes / DevOps

sh
mkdir -p .github/instructions
for name in kubernetes docker github-actions terraform shell; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/${name}.instructions.md" \
    -o ".github/instructions/${name}.instructions.md"
done

Go

sh
mkdir -p .github/instructions
for name in go docker github-actions; do
  curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/${name}.instructions.md" \
    -o ".github/instructions/${name}.instructions.md"
done

For Claude Code, turn the same files into rules in .claude/rules/ (see Claude Code — Repo setup) rather than appending them to CLAUDE.md, where they would load in every session; or let the script do it: setup-ai-agent.sh -t both -p <preset>. In a repository set up for both tools, those rules are the copy Copilot reads too, so skip the .github/instructions/ loops above.

AGENTS.md ​

AGENTS.md at the repository root is the one always-on file every tool reads: Copilot (VS Code, CLI, cloud agent), OpenAI Codex, and Claude Code when there is no CLAUDE.md. The setup script keeps the general instructions there in a marked block and leaves the rest of the file to you:

sh
./setup-ai-agent.sh -t both          # or by hand:
curl -fsSL "https://raw.githubusercontent.com/this-is-tobi/tools/main/ai/common/instructions/general.instructions.md" \
  -o "AGENTS.md"

When the repository also has a CLAUDE.md, Claude Code reads that instead: import the shared file from it with an @AGENTS.md line (the script adds it). Copying the instructions into both files would load them twice in VS Code and Copilot CLI, which read both.

Troubleshooting ​

Instructions not applied (GitHub Copilot)

  • File must be in .github/ (or .github/instructions/ for scoped files)
  • Scoped files must end in .instructions.md
  • Reload VS Code after adding new files or changing settings
  • Check applyTo frontmatter — patterns are glob-style and case-sensitive:
yaml
---
applyTo: "**/*.{js,ts}"
---

Instructions loaded twice

  • Look for copies an older setup left behind: .github/instructions/general.instructions.md or ~/.copilot/instructions/general.instructions.md next to AGENTS.md/copilot-instructions.md, a CLAUDE.md that repeats AGENTS.md, an unmarked copy of the general instructions in ~/.copilot/copilot-instructions.md (the script warns about it), or the same scoped file in both .github/instructions/ and .claude/rules/
  • Re-run the setup script: it removes the copies it installed in earlier runs (backing up the ones you edited) and reports the ones it leaves alone
  • With both tools' agents installed, VS Code's Local agent lists each agent twice: set "chat.agentFilesLocations": { ".claude/agents": false }

Skills not loading

  • Skill files must be named exactly SKILL.md (case-sensitive)
  • Each skill needs its own subdirectory under .github/skills/ (or .claude/skills/ for Claude Code)
  • YAML frontmatter must include name and description
  • Use /skills list in Copilot CLI to verify discovery
  • Use /skills reload after adding skills mid-session

Instruction file too large

Split the content across multiple scoped files. Each one is only loaded when its applyTo pattern matches the file being edited (Copilot) or when you choose to include it (Claude Code). For task-specific workflows, convert to skills instead — they're only loaded when relevant.