Skip to content

slipalison/claupilot

Repository files navigation

claupilot

Use your repository's GitHub Copilot customization from Claude Code and OpenCode — without committing a single tool-specific file.

CI npm License: MIT

Leia em Português (Brasil).

The problem

Your team (or company) standardizes on GitHub Copilot and its .github/ customization layout — copilot-instructions.md, instruction files, prompt files, custom agents, skills. You prefer developing with Claude Code (or OpenCode), but two constraints get in the way:

  1. That tool does not read Copilot's customization files.
  2. You are not allowed to commit anything tool-specific (CLAUDE.md, .claude/, .opencode/, …) to the repo — and duplicating the content by hand rots immediately.

claupilot solves both. It bridges the Copilot structure into your tool locally, at session start, and keeps everything it creates invisible to git. Claude Code is the default target; --target opencode (or both) does the same for OpenCode.

What gets bridged

Copilot (committed, source of truth) Claude Code (local, never committed) How
.github/copilot-instructions.md Session context Injected by a SessionStart hook, as if it were CLAUDE.md
AGENTS.md Session context Injected (Copilot CLI reads both — so does claupilot)
.github/instructions/*.instructions.md .claude/rules/*.md (path-scoped rules) Generated — applyTo globs become paths:, lazily loaded by Claude Code
.github/prompts/*.prompt.md .claude/commands/*.md/name Generated with frontmatter + variable translation
.github/agents/*.md, *.agent.md .claude/agents/*.md (subagents) Generated with tool/model mapping
.github/skills/*/SKILL.md, .agents/skills/ .claude/skills/* Linked (junction/symlink) — same open Agent Skills standard, zero conversion

Everything lands under <repo>/.claude/, which claupilot hides from git via .git/info/exclude — a local, never-committed ignore file. git status stays clean, .gitignore is never touched, and nothing Claude-related can ever leak into a commit or PR.

Install

Requires Node.js ≥ 18 (Claude Code ≥ 2.x).

As a Claude Code plugin (recommended) — inside Claude Code:

/plugin marketplace add slipalison/claupilot
/plugin install claupilot@claupilot

Restart Claude Code. From now on, every session that starts inside a repo with Copilot customization gets bridged automatically. No per-repo setup.

Standalone CLI (no plugin) — run it manually or from your own tooling:

npx @slipalison/claupilot sync          # bridge the current repo (Claude Code)
npx @slipalison/claupilot check         # dry-run: show what would change
npx @slipalison/claupilot context       # preview the context that would be injected
npx @slipalison/claupilot clean         # remove everything claupilot created

npx @slipalison/claupilot sync --target opencode   # bridge for OpenCode instead
npx @slipalison/claupilot sync --target both        # bridge for both tools at once

User-level hook (no plugin, auto-sync) — add to ~/.claude/settings.json:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|clear|compact",
        "hooks": [
          {
            "type": "command",
            "command": "npx --yes @slipalison/claupilot sync --hook",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

How it works (Claude Code)

session start
     │
     ▼
SessionStart hook ──► node claupilot.mjs sync --hook
     │
     ├─ discover  .github/{copilot-instructions.md, instructions/, prompts/, agents/, skills/}, AGENTS.md, .agents/skills/
     ├─ translate frontmatter, tools, models, ${input:*} variables
     ├─ sync      .claude/{agents,commands,rules,skills}   (marker-managed, idempotent)
     ├─ exclude   .git/info/exclude                        (adds a managed "/.claude/" block)
     └─ inject    repo-wide instructions as additionalContext
  • Instruction files become native Claude Code rules (.claude/rules/*.md): the applyTo globs translate to paths: frontmatter, so Claude Code loads each rule only when it touches matching files — the exact semantics Copilot gives applyTo. applyTo: "**" (or none) produces an unconditional rule; nested instruction directories are mirrored. Repo-wide files (copilot-instructions.md, AGENTS.md) are injected as session context.
  • Skills need no translation — Agent Skills is an open standard shared by Copilot and Claude. claupilot links .claude/skills/<name>.github/skills/<name> (junction on Windows — no admin required — symlink on Linux/macOS, full copy as a fallback or with --no-links). Edits to the source are picked up live.
  • Agents and prompts are generated files with a translation layer (see below) and a marker comment identifying them as claupilot-managed.

Translation rules

Tools (Copilot canonical names, their documented aliases, the VS Code chat vocabulary, and MCP):

Copilot Claude Code
execute, shell, bash, powershell Bash (+ PowerShell)
read, NotebookRead Read
edit, write, MultiEdit, NotebookEdit, editFiles Edit, Write (+ NotebookEdit)
search, Grep, Glob, codebase, usages Grep, Glob (+ Read for codebase)
web, WebSearch, WebFetch, fetch WebSearch, WebFetch
agent, custom-agent, Task Agent
todo, TodoWrite TodoWrite
server/tool (MCP) mcp__server__tool

If any token can't be mapped (e.g. server/*), the tools field is omitted entirely — the subagent inherits all tools instead of silently losing a capability. The reason is recorded in a claupilot:notes comment inside the generated file.

Models: claude-sonnet-*sonnet, claude-opus-*opus, claude-haiku-*haiku. GPT/Gemini/other models are dropped (the session model is used) and noted.

Prompt variables: one distinct ${input:x}$ARGUMENTS; multiple → $1, $2, … in order of first appearance (an argument-hint is derived, or passed through when the source declares one). ${selection} becomes a natural-language reference. A custom agent: in a prompt file maps to the bridged subagent of the same name.

OpenCode

Everything above works for OpenCode too — pass --target opencode (or --target both). The same .github/ sources are bridged into OpenCode's native layout under a local, git-excluded .opencode/:

Copilot (committed, source of truth) OpenCode (local, never committed) How
.github/copilot-instructions.md .opencode/opencode.jsoninstructions Referenced in place — no copy; OpenCode resolves the path from the repo root
.github/instructions/*.instructions.md .opencode/opencode.jsoninstructions Referenced in place (load unconditionally — OpenCode has no applyTo scoping)
AGENTS.md (nothing) OpenCode reads it natively
.github/prompts/*.prompt.md .opencode/commands/*.md/name Generated; ${input:*}$ARGUMENTS/$1..$N
.github/agents/*.md, *.agent.md .opencode/agents/*.md (mode: subagent) Generated; Copilot tool allowlist → OpenCode permission map
.github/skills/*/SKILL.md, .agents/skills/ .opencode/skills/* Linked (junction/symlink) — same open Agent Skills standard

Differences from the Claude Code bridge, by design:

  • Instructions are referenced, not copied. OpenCode resolves relative instructions paths from the project root even when they're declared in .opencode/opencode.json, so .github/ stays the single source of truth. Because OpenCode has no applyTo equivalent, instruction files load unconditionally — each file's frontmatter still carries its glob for the model to self-scope.
  • .opencode/opencode.json deep-merges with any opencode.json you already have (OpenCode unions + dedupes instructions), so claupilot never clobbers your config. A pre-existing .opencode/opencode.json that claupilot didn't create is left untouched (skip + warn).
  • Models are dropped (OpenCode uses its configured model) and noted — OpenCode needs a version-pinned provider/model slug that a Copilot hint can't be turned into safely.

Auto-sync (recommended) — install the OpenCode plugin so every session re-bridges on its own, the way the Claude Code plugin does:

mkdir -p ~/.config/opencode/plugin
curl -fsSL https://raw.githubusercontent.com/slipalison/claupilot/main/opencode/plugin/claupilot.js \
  -o ~/.config/opencode/plugin/claupilot.js

The plugin runs claupilot sync --target opencode at session start and also merges the discovered instruction files into the in-memory config so they apply immediately (requires Node ≥ 18 on PATH). Once .opencode/ exists, OpenCode drops its own node_modules/.gitignore inside it — those are OpenCode's, ignored by its own .gitignore, and the whole directory is hidden from git by claupilot anyway.

Guarantees

  • Zero repo footprint. Nothing claupilot writes is visible to git. It edits only .git/info/exclude (which itself is never committed) — never .gitignore.
  • Never touches your files. Generated files carry a claupilot:managed marker; anything without the marker — your own commands, agents, skills — is never modified or deleted.
  • Source of truth stays in .github/. Edit sources, not generated files; the next session start (or /claupilot:sync) re-syncs. Deleted sources have their generated counterparts removed.
  • Idempotent & fast. Unchanged sources → zero writes, typically a few milliseconds.
  • Never breaks a session. In hook mode any internal error is swallowed (logged to stderr) and the session starts normally.
  • Offline & dependency-free. Single-file Node script, no network access, no runtime deps.

Plugin commands

Command What it does
/claupilot:sync Re-sync now and report what changed
/claupilot:doctor Dry-run diagnosis: discovered assets, pending changes, git-exclude status

Good to know

  • First sync in a repo: skills/agents/commands/rules materialized during session start are discovered by Claude Code at the next session start (the injected context covers the gap with MUST-read pointers, and repo-wide instructions are available immediately). Restart or /clear once after the very first bridge of a repo.
  • The reverse direction is native: GitHub Copilot CLI already reads .claude/skills/, CLAUDE.md and .mcp.json. claupilot fills the missing direction — Claude Code reading Copilot's layout.
  • MCP servers: both tools read .mcp.json at the repo root, so MCP config needs no bridging.
  • Prompt files aren't supported by Copilot CLI itself (VS Code only) — claupilot bridges them to Claude Code anyway.
  • Copilot hooks (.github/hooks/*.json) are intentionally not bridged: executing hook commands cross-tool has security implications. May become an opt-in later.
  • Worktrees & monorepos: git worktrees are supported (the exclude block lands in the common git dir); nested .github/ dirs inside monorepo packages are not scanned (only the repo root).
  • Windows: junctions are used instead of symlinks — they work without Developer Mode or admin rights.

Comparison with prior art

rulesync, ruler and dotagent all solve multi-tool config sync by owning the source of truth in their own directory and generating the per-tool files — which are then committed (or force .gitignore edits). claupilot is the opposite shape: Copilot's committed layout stays the source of truth, and the Claude Code / OpenCode side is a disposable, git-invisible, local cache. Nothing to adopt, nothing to commit, nothing for teammates to see.

Development

npm test                 # fixture-based suite (plain node, no framework)
npm run test:coverage    # same suite under c8 (lcov for SonarQube)
claude plugin validate . # validate plugin/marketplace manifests

CI runs the test matrix on Linux and Windows (Node 18/20/24), validates the plugin manifest, does an npm publish dry-run and runs SonarQube with a blocking quality gate. Releases are tag-driven (vX.Y.Z) and publish to npm with provenance.

License

MIT

About

Use your repository's GitHub Copilot customization from Claude Code - without committing a single Claude-specific file

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors