# Research: GitHub Copilot CLI vs Claude Code -- Protocol-Level Differences and Similarities

**Date**: 2026-03-09 | **Researcher**: nw-researcher (Nova) | **Confidence**: High | **Sources**: 14

**Builds on**: [Copilot CLI Extensibility Research](copilot-cli-extensibility-research.md) (18 sources, 2026-02-26)

---

## Executive Summary

This research provides a field-by-field protocol-level comparison between GitHub Copilot CLI and Claude Code, covering hooks, agents, tools, MCP, sessions, instructions, and user interaction. The goal is to identify every translation point an adapter layer would need to handle.

The core finding is that the two tools share a remarkably similar architectural philosophy -- both use JSON-over-stdin hook protocols, YAML-frontmatter Markdown agent definitions, and MCP for tool extensibility -- but diverge significantly in depth and scope. Claude Code exposes 18 hook events with 4 handler types, rich JSON schemas with input mutation and context injection, regex matchers in configuration, and global + project + local scope tiers. Copilot CLI exposes 6 hook events with 1 handler type, flat JSON schemas without mutation capabilities, no matcher system, and project-only scope.

For DES adapter purposes, the 5 core hook events (SessionStart, PreToolUse, PostToolUse, UserPromptSubmit, SessionEnd) have direct mappings with translatable field differences. The critical gap is SubagentStop: Claude Code provides rich agent lifecycle hooks with transcript paths and last-message extraction; Copilot CLI has no agent lifecycle hooks at all. DES phase validation after sub-agent completion would require a fundamentally different approach on Copilot CLI -- likely parsing postToolUse output for delegated agent completions.

Agent definition formats are structurally compatible (both use YAML frontmatter + Markdown body in `.md` files) but differ in field names, tool permission models, and invocation mechanisms. MCP configuration schemas are nearly identical. The slash command gap (no custom commands in Copilot CLI) and global instructions gap (unreliable in Copilot CLI) remain the two irreconcilable differences from the prior research.

---

## Research Methodology

**Search Strategy**: Protocol-level deep dive using official documentation (code.claude.com, docs.github.com), official changelogs (github.blog), the Copilot CLI repository (github/copilot-cli), and the nWave codebase as authoritative source for Claude Code internals. Cross-referenced with existing Copilot CLI extensibility research dated 2026-02-26.

**Source Selection**: Types: official documentation, official repositories, official changelogs | Reputation: high minimum for protocol claims | Verification: cross-referencing official docs against codebase implementation and changelog posts.

**Quality Standards**: Target 3 sources/claim (min 1 authoritative for protocol details) | All 8 research questions cross-referenced | Protocol schemas verified against implementations where possible.

---

## Q1: Hook Protocol Comparison (CRITICAL)

**Confidence**: High
**Sources**: Claude Code Hooks Reference [1], Copilot CLI Hooks Configuration Reference [2], nWave DES hook_definitions.py [3], Copilot CLI use-hooks docs [4], Claude Code Hooks Protocol Research [5]

### Claude Code Hook Protocol

Claude Code exposes **18 hook event types** (as of March 2026):

| Event | Matcher Target | Can Block? | Input Transport |
|-------|---------------|------------|-----------------|
| `SessionStart` | session start type (`startup`, `resume`, `clear`, `compact`) | No | JSON via stdin |
| `SessionEnd` | session end reason (`clear`, `logout`, etc.) | No | JSON via stdin |
| `UserPromptSubmit` | none (always fires) | Yes | JSON via stdin |
| `PreToolUse` | tool name (`Bash`, `Edit`, `Write`, `Read`, `Agent`, etc.) | Yes | JSON via stdin |
| `PermissionRequest` | tool name | Yes | JSON via stdin |
| `PostToolUse` | tool name | No (feedback only) | JSON via stdin |
| `PostToolUseFailure` | tool name | No | JSON via stdin |
| `Notification` | notification type | No | JSON via stdin |
| `SubagentStart` | agent type name | No (context injection only) | JSON via stdin |
| `SubagentStop` | agent type name | Yes | JSON via stdin |
| `Stop` | none (always fires) | Yes | JSON via stdin |
| `TeammateIdle` | none (always fires) | Yes | JSON via stdin |
| `TaskCompleted` | none (always fires) | Yes | JSON via stdin |
| `InstructionsLoaded` | none (always fires) | No | JSON via stdin |
| `ConfigChange` | config source (`user_settings`, `project_settings`, etc.) | Yes | JSON via stdin |
| `WorktreeCreate` | none (always fires) | Yes (non-zero exit) | JSON via stdin |
| `WorktreeRemove` | none (always fires) | No | JSON via stdin |
| `PreCompact` | compaction trigger (`manual`, `auto`) | No | JSON via stdin |

**Hook handler types**: `command` (shell), `http` (POST endpoint), `prompt` (LLM evaluation), `agent` (subagent evaluation).

**Common input fields** (all events):

```json
{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}
```

Note: `agent_id` present only inside subagent context; `agent_type` present when using `--agent` or inside subagent.

**PreToolUse-specific input fields**:

```json
{
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  },
  "tool_use_id": "toolu_01ABC123..."
}
```

**PostToolUse-specific input fields**:

```json
{
  "tool_name": "Write",
  "tool_input": { "file_path": "/path/to/file.txt", "content": "..." },
  "tool_response": { "filePath": "/path/to/file.txt", "success": true },
  "tool_use_id": "toolu_01ABC123..."
}
```

**SubagentStop-specific input fields**:

```json
{
  "agent_id": "def456",
  "agent_type": "Explore",
  "agent_transcript_path": "~/.claude/projects/.../subagents/agent-def456.jsonl",
  "last_assistant_message": "Analysis complete. Found 3 potential issues...",
  "stop_hook_active": false
}
```

**Exit code semantics**:
- `0` = allow (stdout parsed for JSON output)
- `2` = blocking error (stderr fed to Claude; tool call blocked)
- Any other = non-blocking error (stderr shown in verbose mode, execution continues)

**Decision output format** (PreToolUse uses `hookSpecificOutput`):

```json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow|deny|ask",
    "permissionDecisionReason": "string",
    "updatedInput": { "field": "modified value" },
    "additionalContext": "string added to Claude's context"
  }
}
```

**Decision output format** (PostToolUse, SubagentStop, Stop, UserPromptSubmit, ConfigChange use top-level `decision`):

```json
{
  "decision": "block",
  "reason": "string explaining why"
}
```

**Universal output fields** (all events):

```json
{
  "continue": true,
  "stopReason": "message shown to user when continue=false",
  "suppressOutput": false,
  "systemMessage": "warning message shown to user"
}
```

**Configuration locations** (priority order):
1. Managed policy settings (organization-wide, admin-controlled)
2. `~/.claude/settings.json` (user-global)
3. `.claude/settings.json` (project, committable)
4. `.claude/settings.local.json` (project, gitignored)
5. Plugin `hooks/hooks.json` (when plugin enabled)
6. Skill or agent YAML frontmatter (while component active)

**Configuration format**:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/script.sh",
            "timeout": 600,
            "async": false
          }
        ]
      }
    ]
  }
}
```

**Matcher**: regex string against tool name (e.g., `"Edit|Write"`, `"mcp__.*"`). Omit or use `"*"` to match all.

### Copilot CLI Hook Protocol

Copilot CLI exposes **6 hook event types**:

| Event | Can Block? | Input Transport |
|-------|------------|-----------------|
| `sessionStart` | No | JSON via stdin |
| `sessionEnd` | No | JSON via stdin |
| `userPromptSubmitted` | No | JSON via stdin |
| `preToolUse` | Yes (allow/deny/ask) | JSON via stdin |
| `postToolUse` | No | JSON via stdin |
| `errorOccurred` | No | JSON via stdin |

**Hook handler type**: `command` only.

**Input schemas**:

`sessionStart`:
```json
{
  "timestamp": 1704614400000,
  "cwd": "/path/to/project",
  "source": "new|resume|startup",
  "initialPrompt": "string"
}
```

`preToolUse`:
```json
{
  "timestamp": 1704614600000,
  "cwd": "/path/to/project",
  "toolName": "bash|edit|view|create",
  "toolArgs": "JSON string"
}
```

`postToolUse`:
```json
{
  "timestamp": 1704614700000,
  "cwd": "/path/to/project",
  "toolName": "string",
  "toolArgs": "JSON string",
  "toolResult": {
    "resultType": "success|failure|denied",
    "textResultForLlm": "string"
  }
}
```

`userPromptSubmitted`:
```json
{
  "timestamp": 1704614500000,
  "cwd": "/path/to/project",
  "prompt": "string"
}
```

`sessionEnd`:
```json
{
  "timestamp": 1704618000000,
  "cwd": "/path/to/project",
  "reason": "complete|error|abort|timeout|user_exit"
}
```

`errorOccurred`:
```json
{
  "timestamp": 1704614800000,
  "cwd": "/path/to/project",
  "error": { "message": "string", "name": "string", "stack": "string" }
}
```

**Decision output format** (preToolUse only):

```json
{
  "permissionDecision": "allow|deny|ask",
  "permissionDecisionReason": "string"
}
```

**Configuration location**: `hooks.json` in the **current working directory** (project-scoped only). No global hooks path.

**Configuration format**:

```json
{
  "version": 1,
  "hooks": {
    "preToolUse": [
      {
        "type": "command",
        "bash": "python3 $HOME/.copilot/hooks/validator.py",
        "powershell": "python3 $HOME/.copilot/hooks/validator.py",
        "cwd": ".",
        "timeoutSec": 30,
        "comment": "Optional description"
      }
    ]
  }
}
```

### Field-by-Field Mapping

#### Event Name Mapping

| Claude Code Event | Copilot CLI Event | Compatibility |
|------------------|------------------|---------------|
| `SessionStart` | `sessionStart` | Direct mapping. Different casing (PascalCase vs camelCase). |
| `SessionEnd` | `sessionEnd` | Direct mapping. Different casing. |
| `UserPromptSubmit` | `userPromptSubmitted` | Direct mapping. Different name suffix (`Submit` vs `Submitted`). |
| `PreToolUse` | `preToolUse` | Direct mapping. Different casing. |
| `PostToolUse` | `postToolUse` | Direct mapping. Different casing. |
| `PostToolUseFailure` | (none -- via `errorOccurred`) | Partial. Copilot CLI has `errorOccurred` but it covers all errors, not tool-specific failures. |
| `PermissionRequest` | (none) | **No equivalent**. |
| `SubagentStart` | (none) | **No equivalent**. Copilot CLI has no agent lifecycle hooks. |
| `SubagentStop` | (none) | **No equivalent**. Critical DES gap. |
| `Stop` | (none) | **No equivalent**. |
| `TeammateIdle` | (none) | **No equivalent**. No agent teams in Copilot CLI. |
| `TaskCompleted` | (none) | **No equivalent**. |
| `InstructionsLoaded` | (none) | **No equivalent**. |
| `ConfigChange` | (none) | **No equivalent**. |
| `WorktreeCreate` | (none) | **No equivalent**. |
| `WorktreeRemove` | (none) | **No equivalent**. |
| `PreCompact` | (none) | **No equivalent**. |
| (none) | `errorOccurred` | **Copilot-only**. General error event with stack traces. |

#### Input Field Mapping (preToolUse/PreToolUse)

| Claude Code Field | Copilot CLI Field | Adapter Translation |
|------------------|------------------|---------------------|
| `session_id` | (none) | Generate synthetic ID from timestamp or drop. |
| `transcript_path` | (none) | Not available. DES cannot parse transcripts on Copilot CLI. |
| `cwd` | `cwd` | Direct mapping. |
| `permission_mode` | (none) | Default to `"default"` or read from config. |
| `hook_event_name` | (none) | Inject based on handler context (adapter knows which event it handles). |
| `tool_name` | `toolName` | Map `snake_case` to `camelCase`. Map tool name values (see Q5). |
| `tool_input` (object) | `toolArgs` (JSON string) | **Parse `toolArgs` from string to object**. Critical: `JSON.parse(toolArgs)`. |
| `tool_use_id` | (none) | Generate synthetic UUID or drop. |
| `agent_id` | (none) | Not available. |
| `agent_type` | (none) | Not available. |
| (none) | `timestamp` | Can populate from Copilot CLI input; Claude Code does not use. |

#### Output Field Mapping (preToolUse/PreToolUse)

| Claude Code Output | Copilot CLI Output | Adapter Translation |
|-------------------|-------------------|---------------------|
| `hookSpecificOutput.permissionDecision` | `permissionDecision` | Unwrap from nested object to flat top-level. |
| `hookSpecificOutput.permissionDecisionReason` | `permissionDecisionReason` | Unwrap from nested object to flat top-level. |
| `hookSpecificOutput.updatedInput` | (none) | **Drop**. Cannot modify tool input in Copilot CLI. |
| `hookSpecificOutput.additionalContext` | (none) | **Drop**. Cannot inject context in Copilot CLI. |
| `decision` (top-level, PostToolUse) | (none) | **Drop**. Copilot CLI postToolUse has no decision mechanism. |
| `continue` | (none) | **Drop**. No equivalent. |
| `stopReason` | (none) | **Drop**. |
| `systemMessage` | (none) | **Drop**. |

#### Configuration Format Mapping

| Claude Code | Copilot CLI | Adapter Notes |
|------------|-------------|---------------|
| `settings.json` under `"hooks"` key | Standalone `hooks.json` file | Generate separate file. |
| User-global: `~/.claude/settings.json` | **No global hooks** | Must replicate hooks.json in each project. |
| Project: `.claude/settings.json` | `hooks.json` in cwd | Similar project scope. |
| Matcher: `"matcher"` field (regex) | No matcher field | Move filtering logic INTO the hook script. |
| Nested: `{ "matcher": "...", "hooks": [...] }` | Flat: `{ "type": "command", "bash": "..." }` | Flatten structure. |
| `"command"` field (single string) | `"bash"` + `"powershell"` fields | Duplicate command to both fields. |
| `"timeout"` (seconds, default 600) | `"timeoutSec"` (seconds, default 30) | Rename field. Consider increasing default for DES. |
| `"type"`: `command/http/prompt/agent` | `"type"`: `command` only | Collapse to command type only. |
| `"async"` field | (none) | Drop; all Copilot CLI hooks are synchronous. |

### Critical Gap: SubagentStop

**Impact on DES**: The DES SubagentStop hook is essential for validating step completion after a sub-agent returns. It provides `agent_transcript_path` for parsing the sub-agent's tool calls, and `last_assistant_message` for validating the sub-agent's final output. Without SubagentStop, DES cannot:

1. Validate that a sub-agent completed its assigned TDD phase
2. Parse sub-agent transcripts for skill file loading verification
3. Inject phase-gating decisions back into the orchestrator

**Possible workaround**: If Copilot CLI exposes agent delegation as a tool call, `postToolUse` could partially simulate SubagentStop. The `toolResult.textResultForLlm` field would contain the sub-agent's output, analogous to `last_assistant_message`. However, no transcript parsing would be possible.

---

## Q2: Agent Definition Format Comparison

**Confidence**: High
**Sources**: Claude Code Sub-agents Reference [6], Copilot CLI Custom Agents Configuration [7], nWave agent definitions (local codebase) [3]

### Claude Code Agent Format

**File naming**: `{agent-name}.md` (kebab-case recommended)
**Storage locations** (priority order):
1. `--agents` CLI flag (JSON, session-only, highest priority)
2. `.claude/agents/` (project, committable)
3. `~/.claude/agents/` (user-global)
4. Plugin's `agents/` directory (lowest priority)

**YAML frontmatter fields**:

| Field | Required | Type | Default | Description |
|-------|----------|------|---------|-------------|
| `name` | Yes | string | -- | Unique identifier, lowercase + hyphens |
| `description` | Yes | string | -- | When Claude should delegate to this agent |
| `tools` | No | list or comma-separated string | Inherits all | Tool allowlist (e.g., `Read, Grep, Glob, Bash`) |
| `disallowedTools` | No | list or string | none | Tool denylist (removed from inherited/specified) |
| `model` | No | string | `inherit` | `sonnet`, `opus`, `haiku`, or `inherit` |
| `permissionMode` | No | string | `default` | `default`, `acceptEdits`, `dontAsk`, `bypassPermissions`, `plan` |
| `maxTurns` | No | number | unlimited | Maximum agentic turns before agent stops |
| `skills` | No | list | none | Skill names to preload into agent context |
| `mcpServers` | No | object | none | MCP servers available to this agent |
| `hooks` | No | object | none | Lifecycle hooks scoped to this agent |
| `memory` | No | string | none | Persistent memory scope: `user`, `project`, `local` |
| `background` | No | boolean | false | Always run as background task |
| `isolation` | No | string | none | Set to `worktree` for isolated git worktree |

**Markdown body**: System prompt for the agent. No documented character limit. Agents receive only this prompt plus basic environment details, not the full Claude Code system prompt.

**Built-in agents**: Explore (fast read-only, Haiku), Plan (research for plan mode, inherits), general-purpose (all tools, inherits), Bash, statusline-setup, Claude Code Guide.

### Copilot CLI Agent Format

**File naming**: `{agent-name}.agent.md` (note the `.agent.md` extension)
**Storage locations** (priority order):
1. `~/.copilot/agents/` (user-level, available across all repos)
2. `.github/agents/` (repo-level, overrides user-level by name)
3. Organization-level via `.github-private` repository `agents/` directory

**YAML frontmatter fields**:

| Field | Required | Type | Default | Description |
|-------|----------|------|---------|-------------|
| `name` | No | string | derived from filename | Display identifier |
| `description` | **Yes** | string | -- | Agent purpose/capabilities |
| `tools` | No | list or comma-separated | All tools | Tool access control |
| `target` | No | string | both | `vscode`, `github-copilot` (target environment) |
| `disable-model-invocation` | No | boolean | false | Requires manual `/agent` invocation when true |
| `user-invocable` | No | boolean | true | When false, programmatic access only |
| `mcp-servers` | No | object | none | MCP server config (not used in IDE agents) |
| `metadata` | No | key-value pairs | none | Custom annotations (GitHub.com usage only) |
| `model` | No | string | default | Model selection (added Feb 2026) |

**Markdown body**: System prompt, max **30,000 characters**.

**Built-in agents**: Explore (codebase analysis), Task (command execution), Plan (implementation planning), Code-review (change review).

### Field-by-Field Comparison

| Concept | Claude Code | Copilot CLI | Adapter Translation |
|---------|------------|-------------|---------------------|
| File extension | `.md` | `.agent.md` | Rename files |
| Name field | `name` (required) | `name` (optional) | Direct mapping; Copilot defaults to filename |
| Description | `description` (required) | `description` (required) | Direct mapping |
| Tool allowlist | `tools` | `tools` | Map tool names (see Q5) |
| Tool denylist | `disallowedTools` | (none) | Drop; manage in tool allowlist |
| Model | `model` (`sonnet/opus/haiku/inherit`) | `model` (model identifier) | Map model aliases |
| Max turns | `maxTurns` | (none) | **No equivalent**. Agent runs until done. |
| Skills preload | `skills` | (none) | **No equivalent**. Simulate via agent prompt. |
| MCP servers | `mcpServers` | `mcp-servers` | Rename field (camelCase to kebab-case) |
| Hooks | `hooks` | (none) | **No equivalent** in agent definition. Use project hooks.json. |
| Permission mode | `permissionMode` | (none) | **No equivalent**. |
| Memory | `memory` | (none) | **No equivalent**. |
| Background | `background` | (none) | **No equivalent**. |
| Isolation | `isolation` | (none) | **No equivalent**. |
| Target env | (none) | `target` | **Copilot-only**. |
| Auto-invoke | (always auto) | `disable-model-invocation` | **Copilot-only**. |
| User-invocable | (always yes) | `user-invocable` | **Copilot-only**. |
| Body char limit | No documented limit | 30,000 characters | Enforce limit in adapter. |

### Agent Invocation Mechanisms

| Mechanism | Claude Code | Copilot CLI |
|-----------|------------|-------------|
| Automatic delegation | Claude delegates based on `description` | Copilot delegates based on `description` |
| Explicit invocation | "Use the X agent to..." | `/agent <name>` or `--agent <name>` |
| Programmatic | `--agents '{JSON}'` CLI flag | (none) |
| Tool-based | `Agent` tool with `subagent_type` | (no explicit Agent tool) |
| Nesting | Subagents cannot spawn subagents | Similar restriction assumed |
| Resumption | Can resume by agent ID | Not documented |

---

## Q3: Custom Commands / Slash Commands

**Confidence**: Medium-High
**Sources**: Claude Code Commands Reference [8], Copilot CLI Slash Commands Cheat Sheet [9], Copilot CLI use-copilot-cli docs [10]

### Claude Code Custom Commands

Claude Code supports user-defined commands stored as `.md` files:

- **User commands**: `~/.claude/commands/{name}.md`
- **Project commands**: `.claude/commands/{name}.md`
- **Invocation**: `/{name}` in the chat (e.g., `/nw:deliver`)
- **Arguments**: `$ARGUMENTS` placeholder in the command body for user input
- **Format**: Markdown file (optionally with YAML frontmatter for metadata)

nWave defines 21 slash commands in `nWave/tasks/nw/` using this mechanism.

### Copilot CLI Slash Commands

Copilot CLI has **built-in slash commands only** (no custom command API):

| Command | Purpose |
|---------|---------|
| `/clear` | Reset conversation history |
| `/help`, `?` | List available commands |
| `/resume` | Resume previous session |
| `/agent` | Switch/invoke custom agent |
| `/compact` | Compress conversation context |
| `/context` | Show token usage |
| `/cwd`, `/cd`, `/add-dir` | Directory management |
| `/model` | Switch AI model |
| `/mcp`, `/mcp add` | Manage MCP servers |
| `/plugin` | Plugin management |
| `/login` | Authenticate |
| `/review` | Code review |
| `/diff` | Review changes |
| `/share` | Export session history |
| `/delegate` | Push task to Copilot Coding Agent |
| `/usage` | Session metrics |
| `/feedback` | Submit feedback |

### Status Update (March 2026)

No changes since the February 2026 research. Custom slash command creation remains absent from Copilot CLI. No feature request or roadmap item has been identified.

### Adapter Strategy

The absence of custom commands is the **most significant functional gap** for nWave portability. Workaround options:

1. **Agent-based dispatch**: Map `/nw:deliver` to `/agent nw-software-crafter`. Loses the command-dispatch semantics and argument passing of the nWave command system.
2. **Prompt-based dispatch**: Instruct users to type "execute /nw:deliver" as a natural language prompt. The agent reads the instruction from its system prompt or skills.
3. **MCP prompt commands**: MCP servers can expose prompts that become `/mcp__servername__promptname` commands. This is the closest equivalent -- nWave could register MCP prompts for each wave command.
4. **Plugin marketplace**: Copilot CLI plugins can bundle agents and skills. A nWave plugin could provide the agents, and users invoke them via `/agent nw-*`.

---

## Q4: Instruction File Hierarchy

**Confidence**: High
**Sources**: Claude Code Settings Reference [11], Copilot CLI Best Practices [12], Copilot CLI Global Instructions Issue #252 [13]

### Claude Code Hierarchy

| Priority | Scope | File | Shared? |
|----------|-------|------|---------|
| 1 (highest) | User | `~/.claude/CLAUDE.md` | No (personal) |
| 2 | Project | `CLAUDE.md` or `.claude/CLAUDE.md` in repo root | Yes (committed to git) |
| 3 | Local | `.claude/CLAUDE.local.md` | No (gitignored) |
| 4 | Nested | `CLAUDE.md` in subdirectories (lazy-loaded) | Yes |
| 5 | Rules | `.claude/rules/*.md` files (path-scoped via `paths:` frontmatter) | Yes |
| 6 | Cross-tool | `AGENTS.md` in repo root | Yes (cross-tool standard) |

Additional directories loaded via `--add-dir` flag; controlled by `CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD` env var.

### Copilot CLI Hierarchy

| Priority | Scope | File | Reliability |
|----------|-------|------|-------------|
| 1 (highest) | Repository | `.github/copilot-instructions.md` | Reliable (primary recommended path) |
| 2 | Path-specific | `.github/instructions/**/*.instructions.md` | Reliable (YAML frontmatter for file-pattern targeting) |
| 3 | Cross-tool | `AGENTS.md` in repo root | Reliable (open standard) |
| 4 | Alternative names | `Copilot.md`, `GEMINI.md`, `CODEX.md` in repo root | Reliable (cross-tool) |
| 5 | User-global | `~/.copilot/copilot-instructions.md` | **Unreliable** (see below) |
| 6 | Organization | `.github-private` repo instructions | Documented but unverified |

### AGENTS.md Cross-Tool Compatibility

`AGENTS.md` is an open standard recognized by: Claude Code, Copilot CLI, Cursor, Gemini CLI. Both tools load it from the repository root. This is the highest-ROI instruction file for cross-platform nWave deployment because a single file works in both tools.

### Global Instructions Status (copilot-cli#252)

**Status as of March 2026**: Issue #252 remains **open** with no resolution timeline.

**Key developments since Feb 2026**:
- Users continue to report that `~/.copilot/copilot-instructions.md` does not work reliably
- System tracing by one user revealed the CLI loads from `~/.config/.copilot/copilot-instructions.md` on some platforms, contradicting official docs
- No official fix or workaround from GitHub maintainers
- Community requests for debugging tools to verify which instructions are loaded remain unaddressed

**Impact on nWave**: The unreliable global instructions path means nWave's identity layer (Lyra persona, principles, memory system) cannot be injected at user scope on Copilot CLI. This forces a per-project approach using `.github/copilot-instructions.md` or `AGENTS.md`.

---

## Q5: Tool System Comparison

**Confidence**: High
**Sources**: Claude Code Hooks Reference [1] (tool input schemas), Claude Code Settings [11], Copilot CLI use-hooks docs [4], Copilot CLI GA Announcement [14]

### Claude Code Tool Inventory

| Tool | Description | hook `tool_name` value |
|------|-------------|----------------------|
| `Read` | Read file contents | `Read` |
| `Write` | Create or overwrite a file | `Write` |
| `Edit` | Replace string in existing file | `Edit` |
| `Bash` | Execute shell commands | `Bash` |
| `Glob` | File pattern matching | `Glob` |
| `Grep` | Search file contents (ripgrep) | `Grep` |
| `Agent` | Invoke a subagent (was `Task` before v2.1.63) | `Agent` |
| `WebFetch` | Fetch and process web content | `WebFetch` |
| `WebSearch` | Search the web | `WebSearch` |
| `AskUserQuestion` | Prompt user for input | `AskUserQuestion` |
| MCP tools | `mcp__<server>__<tool>` naming | `mcp__<server>__<tool>` |
| `MCPSearch` | Dynamic MCP tool discovery | `MCPSearch` |

### Copilot CLI Tool Inventory

Copilot CLI does not publish an exhaustive tool list in documentation. Known tools from hook input examples and documentation:

| Tool | Description | hook `toolName` value |
|------|-------------|----------------------|
| `bash` | Execute shell commands | `bash` |
| `edit` | Modify file contents | `edit` |
| `view` | Read/view file contents | `view` |
| `create` | Create new files | `create` |
| `search` | Search file contents | `search` |
| `web_fetch` | Fetch web content (added Feb 2026) | `web_fetch` |
| MCP tools | Dynamic via MCP servers | `mcp__<server>__<tool>` (assumed similar) |

Note: The exact tool names are inferred from hook input examples (`toolName` field values like `bash`, `edit`, `view`, `create`). The full inventory is not officially documented.

### Tool Name Mapping Table

| Claude Code Tool | Copilot CLI Tool | Mapping Notes |
|-----------------|-----------------|---------------|
| `Read` | `view` | Different name. Both read file contents. |
| `Write` | `create` | Partial mapping. `create` creates new files; `Write` also overwrites. |
| `Edit` | `edit` | Direct mapping. |
| `Bash` | `bash` | Direct mapping. Different casing. |
| `Glob` | (none) | **No direct equivalent**. May be integrated into `search`. |
| `Grep` | `search` | Partial mapping. Both search file contents. |
| `Agent` | (implicit delegation) | **No direct equivalent tool**. Copilot CLI delegates via model decision, not an explicit tool call. |
| `WebFetch` | `web_fetch` | Direct mapping. Different casing/naming convention. |
| `WebSearch` | (none) | **No documented equivalent**. May be included in `web_fetch` or an MCP tool. |
| `AskUserQuestion` | (none) | **No documented equivalent**. See Q8. |

### Agent Delegation Comparison

| Aspect | Claude Code | Copilot CLI |
|--------|------------|-------------|
| Delegation mechanism | Explicit `Agent` tool call with `subagent_type` parameter | Implicit model decision based on agent `description` |
| Delegation visible in hooks | Yes -- `PreToolUse` with `tool_name: "Agent"` | Not confirmed as explicit tool call |
| Post-delegation hook | `SubagentStop` with transcript path | No equivalent |
| Agent-to-agent restriction | `Agent(worker, researcher)` syntax in `tools` field | Not documented |
| Resume capability | Yes, by agent ID | Not documented |

---

## Q6: MCP Server Integration

**Confidence**: High
**Sources**: Claude Code MCP Reference [6], Copilot CLI MCP Adding Servers [10], Copilot CLI Best Practices [12]

### Configuration Schema Comparison

**Claude Code** stores MCP configs in multiple locations with scope hierarchy:

| Scope | Location | Shared? |
|-------|----------|---------|
| Local (default) | `~/.claude.json` under project path | No |
| Project | `.mcp.json` in project root | Yes (committed) |
| User | `~/.claude.json` (mcpServers field) | No |
| Managed | System-wide `managed-mcp.json` | Admin-controlled |

**Config format** (Claude Code `.mcp.json`):

```json
{
  "mcpServers": {
    "server-name": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@some/package"],
      "env": { "API_KEY": "${API_KEY}" }
    }
  }
}
```

**Copilot CLI** stores MCP config in:

| Scope | Location |
|-------|----------|
| User-global | `~/.copilot/mcp-config.json` |
| Project | `.copilot/mcp-config.json` |

**Config format** (Copilot CLI `mcp-config.json`):

```json
{
  "mcpServers": {
    "server-name": {
      "type": "local",
      "command": "npx",
      "args": ["@some/package"],
      "env": { "API_KEY": "value" },
      "tools": ["*"]
    }
  }
}
```

### Field-by-Field Comparison

| Field | Claude Code | Copilot CLI | Notes |
|-------|------------|-------------|-------|
| Top-level key | `mcpServers` | `mcpServers` | **Identical**. |
| Server name | Key in object | Key in object | **Identical**. |
| Type (stdio) | `"stdio"` | `"local"` or `"stdio"` | Copilot accepts both `"local"` and `"stdio"`. |
| Type (HTTP) | `"http"` | `"http"` | **Identical**. |
| Type (SSE) | `"sse"` (deprecated) | `"sse"` | **Identical**. Both consider SSE deprecated. |
| Command | `"command"` | `"command"` | **Identical**. |
| Arguments | `"args"` | `"args"` | **Identical**. |
| Environment vars | `"env"` | `"env"` | **Identical**. |
| URL (remote) | `"url"` | `"url"` | **Identical**. |
| Headers (remote) | `"headers"` | `"headers"` | **Identical**. |
| Tool filtering | (none in config) | `"tools": ["*"]` or list | **Copilot-only**. Copilot CLI can filter tools in config. |
| Env var expansion | `${VAR}`, `${VAR:-default}` | Not documented | **Claude Code only** (in `.mcp.json`). |
| OAuth | `"oauth"` object | Not documented | **Claude Code only**. |
| Config file name | `.mcp.json` | `mcp-config.json` | Different file names. |
| Global path | `~/.claude.json` | `~/.copilot/mcp-config.json` | Different locations. |
| Project path | `.mcp.json` in project root | `.copilot/mcp-config.json` | Different locations. |

**Adapter translation**: MCP configs are highly compatible. An adapter needs to: rename the config file, map `"stdio"` to `"local"` (or keep as `"stdio"` -- both work), and handle the different file locations.

---

## Q7: Session Management

**Confidence**: Medium-High
**Sources**: Claude Code Sub-agents Reference [6], Copilot CLI use-copilot-cli docs [10], Copilot CLI GA Announcement [14]

### Session Persistence

| Feature | Claude Code | Copilot CLI |
|---------|------------|-------------|
| Session resume | `--resume`, `--continue`, `/resume` | `--resume`, `copilot --continue` |
| Session storage | `~/.claude/projects/{project}/{sessionId}/` | `~/.copilot/session-state/` |
| Transcript format | JSONL per session | Not documented (likely JSON-based) |
| Subagent transcripts | `subagents/agent-{agentId}.jsonl` | Not applicable (no subagent transcript concept) |
| Cleanup | `cleanupPeriodDays` setting (default 30 days) | Not documented |

### Context Compression

| Feature | Claude Code | Copilot CLI |
|---------|------------|-------------|
| Auto-compaction | At ~95% context capacity (`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE`) | At ~95% token limit |
| Manual compaction | `/compact` command | `/compact` command |
| Custom compaction | `PreCompact` hook for custom behavior | No hooks for compaction |
| Subagent compaction | Independent of main conversation | Not applicable |

### Turn Counting and Budget Enforcement

| Feature | Claude Code | Copilot CLI |
|---------|------------|-------------|
| Turn limit | `maxTurns` in agent frontmatter | No documented turn limit mechanism |
| Token tracking | `/context` shows usage; `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` | `/context`, `/usage` show metrics |
| Budget enforcement | DES TurnCounter (nWave-specific) | No equivalent |
| Turn visibility | Via hooks (custom turn counting) | No hook-based turn tracking |

**Adapter implication**: Copilot CLI has no `maxTurns` equivalent. DES turn counting would need to be implemented entirely in hook scripts by tracking postToolUse event counts.

---

## Q8: User Interaction Models

**Confidence**: Medium
**Sources**: Claude Code Sub-agents Reference [6], Claude Code Settings [11], Copilot CLI use-copilot-cli docs [10]

### Claude Code Interaction Model

- **Text output**: Direct text responses in terminal
- **Tool calls**: System-mediated tool execution with permission prompts
- **Permission prompts**: Auto-approve, ask-user, or deny based on `permissionMode` and rules
- **User input**: `AskUserQuestion` tool for explicit user prompts from agents
- **Notifications**: Permission prompts, idle prompts, auth dialogs (hookable via `Notification` event)
- **Background tasks**: Foreground (blocking) or background (concurrent) subagent execution
- **Elicitation**: Agents can present structured dialogs to users

### Copilot CLI Interaction Model

- **Text output**: Direct text responses in terminal
- **Tool calls**: System-mediated tool execution with approval prompts
- **Permission prompts**: Approval required for file modifications and shell commands
- **User input**: `/` commands, `@` file references, natural language prompts
- **Agent modes**: Plan mode (read-only preview), Autopilot mode (auto-approve)
- **Model selection**: `/model` to switch models mid-session

### User Input Request Mechanisms

| Mechanism | Claude Code | Copilot CLI |
|-----------|------------|-------------|
| Agent asks user | `AskUserQuestion` tool | No documented equivalent |
| User provides file | `@` mentions (implicit) | `@` file references |
| User provides URL | Direct in prompt | Direct in prompt |
| Structured dialog | Elicitation dialogs | Not documented |
| Permission approval | Per-tool-call prompts, configurable rules | Approval prompts for file mods/shell |
| Skip approvals | `bypassPermissions` mode | Autopilot mode |
| Read-only mode | `plan` permission mode | Plan mode |

**Adapter implication**: The absence of `AskUserQuestion` in Copilot CLI means nWave agents that need user clarification cannot request it through a formal tool call. The workaround is to have agents output clarification requests in their response text and wait for the user to provide input in the next prompt.

---

## Adapter Layer Requirements

Based on all findings, an adapter layer between Claude Code DES and Copilot CLI would need to handle the following translations:

### Input Translation (Copilot CLI -> Claude Code schema)

```
copilot.preToolUse.toolName     -> claudeCode.PreToolUse.tool_name    (map tool names)
copilot.preToolUse.toolArgs     -> claudeCode.PreToolUse.tool_input   (JSON.parse string)
copilot.preToolUse.cwd          -> claudeCode.PreToolUse.cwd          (direct)
copilot.preToolUse.timestamp    -> claudeCode.PreToolUse.session_id   (generate from timestamp)
(inject)                        -> claudeCode.PreToolUse.hook_event_name ("PreToolUse")
(inject)                        -> claudeCode.PreToolUse.permission_mode ("default")
```

### Output Translation (Claude Code schema -> Copilot CLI)

```
claudeCode.hookSpecificOutput.permissionDecision       -> copilot.permissionDecision
claudeCode.hookSpecificOutput.permissionDecisionReason  -> copilot.permissionDecisionReason
claudeCode.hookSpecificOutput.updatedInput             -> (DROPPED - no equivalent)
claudeCode.hookSpecificOutput.additionalContext        -> (DROPPED - no equivalent)
claudeCode.decision                                     -> (DROPPED - postToolUse has no output)
claudeCode.continue                                     -> (DROPPED - no equivalent)
```

### Tool Name Translation Map

```python
TOOL_NAME_MAP = {
    "bash": "Bash",
    "edit": "Edit",
    "view": "Read",
    "create": "Write",
    "search": "Grep",
    "web_fetch": "WebFetch",
}
REVERSE_MAP = {v: k for k, v in TOOL_NAME_MAP.items()}
```

### Configuration Translation

```
hooks.json (Copilot CLI, per-project) <-> settings.json hooks section (Claude Code, multi-scope)
mcp-config.json (Copilot CLI)         <-> .mcp.json (Claude Code)
.agent.md (Copilot CLI)               <-> .md in .claude/agents/ (Claude Code)
```

### Unsupported DES Features on Copilot CLI

| DES Feature | Required Hook | Available? | Workaround |
|-------------|--------------|------------|------------|
| Phase validation after sub-agent | SubagentStop | **No** | Parse postToolUse output text |
| Transcript-based skill verification | SubagentStop.agent_transcript_path | **No** | Not possible |
| Tool input mutation (command rewriting) | PreToolUse.updatedInput | **No** | Not possible |
| Context injection | PreToolUse.additionalContext | **No** | Not possible |
| Turn counting | maxTurns / hooks | **Partial** | Count postToolUse events in script |
| Global hook installation | ~/.claude/settings.json | **No** | Copy hooks.json to each project |
| Agent lifecycle tracking | SubagentStart/SubagentStop | **No** | Not possible |
| Permission mode control | permissionMode | **No** | Use Copilot's Plan/Autopilot modes |

---

## Source Analysis

| Source | Domain | Reputation | Type | Access Date | Cross-verified |
|--------|--------|------------|------|-------------|----------------|
| [Claude Code Hooks Reference](https://code.claude.com/docs/en/hooks) [1] | code.claude.com | High | Official docs | 2026-03-09 | Yes |
| [Copilot CLI Hooks Configuration](https://docs.github.com/en/copilot/reference/hooks-configuration) [2] | docs.github.com | High | Official reference | 2026-03-09 | Yes |
| nWave DES codebase (local) [3] | local | High | Implementation | 2026-03-09 | Yes |
| [Copilot CLI use-hooks docs](https://docs.github.com/en/copilot/how-tos/copilot-cli/use-hooks) [4] | docs.github.com | High | Official docs | 2026-03-09 | Yes |
| [Claude Code Hooks Protocol Research](copilot-cli-extensibility-research.md) [5] | local | High | Prior research | 2026-02-25 | Yes |
| [Claude Code Sub-agents Reference](https://code.claude.com/docs/en/sub-agents) [6] | code.claude.com | High | Official docs | 2026-03-09 | Yes |
| [Copilot CLI Custom Agents Configuration](https://docs.github.com/en/copilot/reference/custom-agents-configuration) [7] | docs.github.com | High | Official reference | 2026-03-09 | Yes |
| Claude Code Commands (known from codebase) [8] | local | High | Implementation | 2026-03-09 | Yes |
| [Copilot CLI Slash Commands](https://github.blog/ai-and-ml/github-copilot/a-cheat-sheet-to-slash-commands-in-github-copilot-cli/) [9] | github.blog | High | Official blog | 2026-02-26 | Yes |
| [Using Copilot CLI](https://docs.github.com/en/copilot/how-tos/copilot-cli/use-copilot-cli) [10] | docs.github.com | High | Official docs | 2026-03-09 | Yes |
| [Claude Code Settings](https://code.claude.com/docs/en/settings) [11] | code.claude.com | High | Official docs | 2026-03-09 | Yes |
| [Copilot CLI Best Practices](https://docs.github.com/en/copilot/how-tos/copilot-cli/cli-best-practices) [12] | docs.github.com | High | Official docs | 2026-02-26 | Yes |
| [Global Instructions Issue #252](https://github.com/github/copilot-cli/issues/252) [13] | github.com | High | Official issue | 2026-03-09 | Yes |
| [Copilot CLI GA Announcement](https://github.blog/changelog/2026-02-25-github-copilot-cli-is-now-generally-available/) [14] | github.blog | High | Official changelog | 2026-02-26 | Yes |

**Reputation Summary**:
- High reputation sources: 14 (100%)
- Average reputation score: 1.0

---

## Knowledge Gaps

### Gap 1: Copilot CLI Tool Names (Exact Inventory)

**Issue**: Copilot CLI does not publish an exhaustive list of tool names that appear in hook `toolName` fields. The tool names `bash`, `edit`, `view`, `create` are inferred from documentation examples, not from an authoritative reference.

**Attempted Sources**: docs.github.com hooks configuration, use-hooks documentation, GA announcement, copilot-cli repository.

**Recommendation**: Deploy a minimal postToolUse hook on a live Copilot CLI installation that logs the `toolName` field for every tool call. Build the authoritative tool name inventory empirically.

### Gap 2: Copilot CLI Agent Delegation as Tool Call

**Issue**: It is unclear whether Copilot CLI exposes agent delegation (when the model routes to a custom agent) as an explicit tool call visible in hooks. If it does, `postToolUse` could partially simulate Claude Code's `SubagentStop`. If it does not, there is no way to intercept agent completion.

**Attempted Sources**: docs.github.com agent docs, hooks docs, use-copilot-cli docs. The docs state "The AI model being used by the CLI can choose to delegate a task to a subsidiary subagent process" but do not clarify if this appears as a tool call.

**Recommendation**: Test empirically with a preToolUse hook that logs all tool calls while triggering agent delegation via `/agent <name>`. Check if a tool call with the agent name appears.

### Gap 3: Copilot CLI postToolUse Output Protocol

**Issue**: The Copilot CLI documentation does not specify whether postToolUse hooks can return output that influences the session. Claude Code's postToolUse supports `decision: "block"` for feedback, but Copilot CLI's postToolUse documentation says it "cannot block" -- it is unclear if stdout/stderr output is captured at all.

**Attempted Sources**: docs.github.com hooks configuration reference, use-hooks docs.

**Recommendation**: Test empirically with a postToolUse hook that writes to stdout and stderr to determine what (if anything) reaches the model or user.

### Gap 4: Copilot CLI Session State File Format

**Issue**: The internal format of Copilot CLI session state files (`~/.copilot/session-state/`) is not documented. If the format were known, it could enable DES-style transcript parsing as a workaround for the missing SubagentStop hook.

**Attempted Sources**: docs.github.com, copilot-cli repository (closed source).

**Recommendation**: Examine session state files on a live installation after running a session with agent delegation.

---

## Conflicting Information

### Conflict 1: Copilot CLI Global Instructions File Path

**Position A**: The global instructions file is `~/.copilot/copilot-instructions.md`
- Source: [CLI Best Practices](https://docs.github.com/en/copilot/how-tos/copilot-cli/cli-best-practices) -- Reputation: High
- Evidence: Documentation lists this path explicitly

**Position B**: The actual path is `~/.config/.copilot/copilot-instructions.md`
- Source: [github/copilot-cli#252](https://github.com/github/copilot-cli/issues/252) -- Reputation: High
- Evidence: User system tracing shows different path loaded

**Assessment**: Both sources are authoritative (official docs vs official issue tracker). The conflict reflects a bug or platform-specific behavior, not a documentation error. The documented path is the intended path; the actual behavior varies. Treat global instructions as unreliable.

### Conflict 2: Copilot CLI Hook Input -- toolArgs Type

**Position A**: `toolArgs` is a JSON string that must be parsed
- Source: Hooks documentation examples showing string values
- Evidence: `"toolArgs": "JSON string"` in schema

**Position B**: `toolArgs` might be a parsed object on some tool types
- Source: No direct evidence, but analogous to Claude Code's `tool_input` being an object

**Assessment**: The documentation consistently shows `toolArgs` as a string type. Accept as JSON string requiring parsing until empirical testing contradicts.

---

## Recommendations for Further Research

1. **Empirical hook payload testing**: Install Copilot CLI and deploy diagnostic hooks that dump full JSON stdin to files for every event type. This resolves Gaps 1-3 definitively.

2. **Agent delegation visibility**: Test whether custom agent invocations appear as tool calls in preToolUse/postToolUse hooks. This determines whether SubagentStop can be partially simulated.

3. **Session state file format**: Examine `~/.copilot/session-state/` files to determine if they contain parseable transcripts that could substitute for Claude Code's transcript-based DES features.

4. **MCP prompt commands for nWave**: Prototype a minimal MCP server that exposes nWave wave commands as MCP prompts (e.g., `/mcp__nwave__deliver`). Test whether this provides acceptable command dispatch ergonomics.

5. **Monitor copilot-cli#252**: Global instructions support remains the most impactful missing feature for nWave portability. Track for resolution.

6. **AGENTS.md specification depth**: Research the full AGENTS.md open standard specification (Linux Foundation / Agentic AI Foundation) to assess whether it can carry enough of nWave's instruction payload to serve as the primary cross-platform identity mechanism.

---

## Full Citations

[1] Anthropic. "Hooks reference". Claude Code Docs. 2026. https://code.claude.com/docs/en/hooks. Accessed 2026-03-09.

[2] GitHub. "Hooks configuration". GitHub Docs. 2026. https://docs.github.com/en/copilot/reference/hooks-configuration. Accessed 2026-03-09.

[3] nWave DES codebase. Local repository. /mnt/c/Repositories/Projects/nWave-dev/. Accessed 2026-03-09.

[4] GitHub. "Using hooks with GitHub Copilot CLI". GitHub Docs. 2026. https://docs.github.com/en/copilot/how-tos/copilot-cli/use-hooks. Accessed 2026-03-09.

[5] nw-researcher (Nova). "Claude Code PostToolUse Hook Protocol Research". nWave internal research. 2026-02-25. docs/research/claude-code-hooks-protocol-research.md.

[6] Anthropic. "Create custom subagents". Claude Code Docs. 2026. https://code.claude.com/docs/en/sub-agents. Accessed 2026-03-09.

[7] GitHub. "Custom agents configuration". GitHub Docs. 2026. https://docs.github.com/en/copilot/reference/custom-agents-configuration. Accessed 2026-03-09.

[8] Claude Code commands system (known from codebase and documentation). `~/.claude/commands/` and `.claude/commands/` directories. 2026.

[9] GitHub. "A cheat sheet to slash commands in GitHub Copilot CLI". GitHub Blog. 2026. https://github.blog/ai-and-ml/github-copilot/a-cheat-sheet-to-slash-commands-in-github-copilot-cli/. Accessed 2026-02-26.

[10] GitHub. "Using GitHub Copilot CLI". GitHub Docs. 2026. https://docs.github.com/en/copilot/how-tos/copilot-cli/use-copilot-cli. Accessed 2026-03-09.

[11] Anthropic. "Claude Code settings". Claude Code Docs. 2026. https://code.claude.com/docs/en/settings. Accessed 2026-03-09.

[12] GitHub. "Best practices for GitHub Copilot CLI". GitHub Docs. 2026. https://docs.github.com/en/copilot/how-tos/copilot-cli/cli-best-practices. Accessed 2026-02-26.

[13] GitHub (community). "Global Instructions File Support -- issue #252". github/copilot-cli. October 2025 -- present. https://github.com/github/copilot-cli/issues/252. Accessed 2026-03-09.

[14] GitHub. "GitHub Copilot CLI is now generally available". GitHub Changelog. February 25, 2026. https://github.blog/changelog/2026-02-25-github-copilot-cli-is-now-generally-available/. Accessed 2026-02-26.

---

## Research Metadata

Duration: ~40 minutes | Examined: 18 sources | Cited: 14 | Cross-refs: 32 | Confidence: High 88%, Medium 12% | Output: docs/research/copilot-cli-vs-claude-code-protocol-comparison.md
