| name | dual-harness |
| description | CRITICAL: Load this skill before ANY code change in the Root framework. Root is a dual-harness project — every file must work for both Claude Code (plugin) and Gemini CLI (extension). This skill contains the rules for shared vs harness-specific files, command naming, config schemas, environment variables, and CLI usage. Load when: editing any file in this repo, adding commands/skills/agents/hooks, modifying plugin.json or gemini-extension.json, changing scripts or configs, working with mcp-local-rag. |
| user-invocable | false |
Root Dual-Harness Development Rules
Root ships as BOTH a Claude Code plugin AND a Gemini CLI extension. Every change must work for both. These rules are non-negotiable.
Architecture: What Is Shared, What Is Not
Shared (both harnesses use these)
commands/root/*.toml + commands/root/*.md — slash commands
skills/*/SKILL.md — model-invoked skills
agents/*.md — agent templates
scripts/*.sh — shell scripts
hooks/scripts/*.sh — hook scripts
.mcp.json — MCP server config (Claude reads this directly; Gemini inlines its own copy)
root.config.json — per-project config in consumer repos
.root/rag-db — shared RAG database location
Claude-specific
.claude-plugin/plugin.json — Claude plugin manifest
.claude-plugin/marketplace.json — marketplace entry
.claude-plugin/hooks.json — Claude hook wiring
Gemini-specific
gemini-extension.json — Gemini extension manifest
hooks/gemini-hooks.json — Gemini hook wiring
Environment Variables
In shared files (commands, scripts, skills):
- Use ONLY universal variables:
$HOME, $PWD, $PATH
- NEVER use
${CLAUDE_PLUGIN_ROOT} — Gemini doesn't set it
- NEVER use
${extensionPath} — Claude doesn't set it
- NEVER use
${CLAUDE_SKILL_DIR} — Gemini doesn't set it
In harness-specific files:
- Claude hooks/configs CAN use
${CLAUDE_PLUGIN_ROOT}
- Gemini hooks/configs CAN use
${extensionPath} (NOT ${GEMINI_EXTENSION_DIR})
Command Naming
The two harnesses derive command names differently:
Gemini CLI: Derives from directory structure. commands/root/rag.toml → /root:rag
Claude Code: Plugin name auto-prefixes. plugin.json → "commands": "./commands/root" → /root:rag
This is why:
- Commands MUST live in
commands/root/ (Gemini needs the subdirectory for namespacing)
plugin.json MUST point "commands" at "./commands/root" (not "./commands", which causes root:root:rag)
- TOML
name field (e.g. name = "root:rag") is ignored by Gemini — it uses the file path
- TOML
prompt field must be relative to repo root: prompt = "commands/root/rag.md"
Never move commands out of commands/root/. Never change the plugin.json commands path.
Command Prompt Files (.md)
Command prompts are shared between both harnesses. The model reads them and executes bash.
Rules for inline bash in prompts:
- Use
$HOME-based paths to reference shared binaries
- The RAG binary is always at:
${HOME}/.root-framework/mcp/node_modules/mcp-local-rag/dist/index.js
- Never reference plugin/extension install directories
TOML Command Files
Claude ignores TOML files entirely — it reads the .md files directly.
Gemini reads the TOML prompt field and executes it. The prompt content must be inline in the TOML as a multi-line string (triple quotes), NOT a file path reference. Gemini cannot resolve external file references.
Both locations must have identical content:
.md file — Claude reads this
.toml file prompt field — Gemini reads this
When updating a command, update both the .md and .toml files. They must stay in sync.
TOML format:
name = "root:commandname"
description = "..."
prompt = """
[full command prompt content here — same as the .md file]
"""
Gemini uses: description, prompt
Gemini ignores: name, type
Command Autocomplete Descriptions
Claude Code and Gemini read the command description from different places:
- Gemini: TOML
description field
- Claude: YAML frontmatter at the top of the
.md file
Every command .md file MUST start with frontmatter:
---
description: <same text as TOML description>
argument-hint: <subcmd1> | <subcmd2> | <subcmd3>
---
# /root:commandname — Title
...
The description shown in Claude's / autocomplete comes from frontmatter. Without it, Claude falls back to the first H1, which is verbose and inconsistent with Gemini's display.
The argument-hint appears as a placeholder after the user types the command and a space. Omit it for commands with no subcommands (like /root:init).
Sync rule: description lives in 3 places. When editing a command description, update all three:
commands/root/<name>.toml → description (for Gemini)
commands/root/<name>.md → frontmatter description (for Claude)
- The H1 on the first content line of the
.md (human-readable title in the prompt body)
Keep 1 and 2 identical. The H1 can be a shorter human title.
Version Sync
Three files must have the same version on every bump:
.claude-plugin/plugin.json → "version"
.claude-plugin/marketplace.json → plugins[0].version
gemini-extension.json → "version"
Claude caches plugins by version. If you change code without bumping version, users won't get updates.
RAG Database
One shared location: .root/rag-db (relative to consumer project root).
Both harnesses' MCP configs point here. Never use .claude/rag-db or .gemini/rag-db.
The root.config.json default for ingest.dbPath is .root/rag-db.
mcp-local-rag CLI
Verified behavior (from --help output):
Global options go BEFORE the subcommand:
node "$RAG_BIN" --db-path "$DB_PATH" --cache-dir "$CACHE_DIR" ingest <directory>
NOT:
node "$RAG_BIN" ingest --db-path "$DB_PATH" ...
Ingestion model: root.config.json uses ingest.docs (doc directories only). Directories are ingested recursively. Source code directories are NOT ingested — only docs matter for RAG.
Supported CLI commands: ingest, query, list, status, delete, skills install
Do not guess at CLI behavior. Run --help first.
Plugin Manifest (plugin.json)
Verified fields (from Claude Code docs):
commands: string or array — paths to command files/directories
agents: string or array — paths to individual .md files (NOT directories)
skills: string or array — paths to skill directories
hooks: string, array, or object — hook config paths or inline config
mcpServers: string, array, or object — MCP config paths or inline config
agents must list individual files: "./agents/team-architect.md", NOT "./agents"
The Golden Rule
Do not guess. Verify.
Before changing any config, CLI invocation, or manifest field:
- Check
--help output for CLIs
- Check official documentation for schemas
- Test against a real database/installation
- Confirm the change works for BOTH harnesses