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
.gitignoreso git ignores them - Manifest:
.teamide/subrepos.jsontracks which subrepos are linked
Adding a Subrepo
- Open a project in the Code module
- Click the link icon in the Explorer toolbar (top of file tree)
- 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
- Click Add
What Happens
- If the repo isn’t in the pool yet, it gets cloned to
~/TeamIDE/owner/repo/ - A symlink is created at the specified path in your project
- The path is added to
.gitignore - The entry is saved to
.teamide/subrepos.json
Removing a Subrepo
- Right-click the subrepo folder in the file tree
- Select Remove Subrepo
- Confirm the removal
What Gets Removed
- The symlink in your project
- The
.gitignoreentry - 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.jsonand 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/repoinventory
=== 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:
- Reads the manifest
- Recreates any missing symlinks (e.g., after a fresh clone)
- Scaffolds Claude Code hooks if not already present
- 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 |
Related
- Git Integration - Staging, commits, branches
- Local Repositories - Working with local folders
- Gitea Repositories - Self-hosted repos
Changelog
| Date | Change |
|---|---|
| 2026-03-13 | Initial implementation |