Subrepos

Symlink-based repository linking that replaces git submodules

Overview

Subrepos let you link other repositories into your project without git submodules. Each repo exists once in the pool at ~/TeamIDE/owner/repo/, and when a parent project needs it, TeamIDE creates a symlink. This gives you:

  • Independent git histories per repo (commits stay separate)
  • No submodule hassles (detached HEAD, recursive init, etc.)
  • Shared pool means no duplicate clones
  • Claude Code sees subrepo files natively through symlinks

How It Works

~/TeamIDE/
  dcherrera/
    TeamIDE/                    <- parent repo (real)
      .teamide/
        subrepos.json           <- manifest
      .gitignore                <- auto-appended: /TeamIDE-Server
      TeamIDE-Server/           <- SYMLINK -> ~/TeamIDE/dcherrera/TeamIDE-Server
    TeamIDE-Server/             <- pool repo (real, independent clone)
  • Pool: Every repo lives at ~/TeamIDE/owner/repo/ as a full git clone
  • Symlink: Parent projects get a symlink pointing to the pool repo
  • Gitignore: Symlink paths are auto-added to .gitignore so git ignores them
  • Manifest: .teamide/subrepos.json tracks which subrepos are linked

Adding a Subrepo

  1. Open a project in the Code module
  2. Click the link icon in the Explorer toolbar (top of file tree)
  3. In the Add Subrepo dialog:
    • Select repo from pool: Type to search existing repos in your pool
    • Selecting a repo auto-fills URL, path, and branch
    • Or manually enter a Repository URL and Local path
  4. Click Add

What Happens

  1. If the repo isn’t in the pool yet, it gets cloned to ~/TeamIDE/owner/repo/
  2. A symlink is created at the specified path in your project
  3. The path is added to .gitignore
  4. The entry is saved to .teamide/subrepos.json

Removing a Subrepo

  1. Right-click the subrepo folder in the file tree
  2. Select Remove Subrepo
  3. Confirm the removal

What Gets Removed

  • The symlink in your project
  • The .gitignore entry
  • The manifest entry

What Stays

  • The pool repo at ~/TeamIDE/owner/repo/ is never deleted (other projects may use it)

File Tree

Subrepo directories show a link badge in the file tree:

Badge Color Icon Meaning
Green check Clean, up to date
Orange edit Has uncommitted changes
Blue arrow_upward Ahead of remote
Purple arrow_downward Behind remote
Red sync_problem Diverged from remote
Red link_off Broken symlink (pool repo missing)

Changes Panel

When a subrepo has uncommitted changes, it appears as a tab in the Changes panel alongside the parent repo. Each tab shows:

  • The repo name with a link icon
  • Number of changed files
  • Independent staging and commit (commits go to the subrepo’s own git history)

Manifest Format

The manifest lives at .teamide/subrepos.json:

{
  "subrepos": [
    {
      "owner": "dcherrera",
      "repo": "TeamIDE-Server",
      "path": "TeamIDE-Server",
      "branch": "main",
      "url": "https://github.com/dcherrera/TeamIDE-Server.git"
    }
  ]
}

Claude Code Integration

TeamIDE auto-scaffolds Claude Code hooks at SessionStart so Claude always has context about the project environment. Hook scripts live in .claude/hooks/ and are registered in .claude/settings.json.

Subrepo Hook (conditional)

Installed when the first subrepo is added; removed when the last is deleted.

  • Script: .claude/hooks/subrepo-context.sh
  • Output: Reads .teamide/subrepos.json and reports each subrepo’s symlink health and git status
=== Subrepos (symlink-based linked repos) ===
Manifest: .teamide/subrepos.json

- TeamIDE-Server at ./TeamIDE-Server (clean)
- RWKVx at ./RWKVx (DIRTY)
     M src/model.py
     ?? experiments/new.py

This works even outside TeamIDE — anyone cloning the repo and using Claude Code gets subrepo awareness. If the symlinks don’t exist (no pool repos), Claude sees them as MISSING and can advise the user.

Repo Pool Hook (always-on)

Installed on every project open. Lists all repos available in the local ~/TeamIDE/ pool so Claude knows what’s cloned locally.

  • Script: .claude/hooks/repo-pool-context.sh
  • Output: Compact owner/repo inventory
=== Repo Pool (~/TeamIDE/) ===
Available repos:
  - dcherrera/TeamIDE
  - dcherrera/TeamIDE-Server
  - dcherrera/RWKVx

This lets Claude suggest linking an existing pool repo as a subrepo, or reference code in sibling repos, without needing an MCP call.

TeamIDE CLI Hook (always-on)

Installed on every project open. Shows available CLI commands and auth status.

  • Script: .claude/hooks/teamide-cli-context.sh
  • Output: Command reference and current auth/workspace info

Hook Architecture

Hook definitions live in backend/services/native-backend/src/hooks/:

File Hook ID Condition
teamide-cli.js teamide-cli Always
subrepo.js subrepo .teamide/subrepos.json exists with entries
repo-pool.js repo-pool Always

The orchestrator (claudeHooks.js) loads all hook modules, manages .claude/settings.json, and exposes installHook(), uninstallHook(), updateHook(), and installAllHooks().

MCP Server Tool

The TeamIDE CLI (teamide mcp) also exposes a teamide_list_subrepos MCP tool. When Claude Code has the TeamIDE MCP server configured, it can call this tool to discover and check subrepo status at any time — not just at session start.


Startup Recovery

On app startup, TeamIDE automatically:

  1. Reads the manifest
  2. Recreates any missing symlinks (e.g., after a fresh clone)
  3. Scaffolds Claude Code hooks if not already present
  4. Reports broken links if a pool repo was deleted

Subrepos vs Git Submodules

Feature Subrepos Git Submodules
Independent commits Yes Yes (but detached HEAD issues)
Requires git submodule init No Yes
Works with any git tool Yes (symlinks are transparent) Needs submodule-aware tools
Tracked in parent git No (gitignored) Yes (.gitmodules)
Shared across projects Yes (pool) No (each clone is separate)
Claude Code support Native (follows symlinks) Native

Note: The Workspace module also provides a “Subrepos” tab for managing git submodules within individual repos. These are different systems — subrepos (this page) use symlinks from the shared pool, while git submodules are tracked in .gitmodules and managed through git itself.


Backend API

Method Path Purpose
GET /subrepos/pool List all repos in the pool
GET /subrepos/:projectId List subrepos for a project
POST /subrepos/:projectId/add Add a subrepo
DELETE /subrepos/:projectId/remove Remove a subrepo
POST /subrepos/:projectId/sync Recreate missing symlinks

Changelog

Date Change
2026-03-13 Initial implementation