agentsclimarketplace

Nested repos

Skill GhostlyGawd/recursive-harness/skills/nested-repos

Portable, evidence-driven agent development harness for Codex, Claude Code, and generic Agent Skills. Active beta v0.1.2.

Install
npx -y skills add GhostlyGawd/recursive-harness --skill nested-repos

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Choose HOW one git repo lives inside another — submodule vs subtree vs gitignore+materialize-hook vs vendor-and-commit. Trigger when you reach for `git submodule`/`git subtree`, weigh a "repo inside a repo", need a sub-project (plugin/lib with its own repo) to ALSO live here AND ride into worktrees, or want a vendored dep that stays updatable. Each trades worktree presence vs in-place dev vs history ownership; pick blind → empty submodule, or a plugin missing from worktrees. NB `.worktreeinclude` canNOT carry a nested-repo dir (verified). Defers to vendoring-skills + worktree.

SKILL.md

9.6 KB, as published. Nobody here has run it

Nested repos — one git repo living inside another

You have a self-contained sub-project (a plugin, a shared lib, a third-party skill, a repo with its own GitHub remote) and it must ALSO live inside this repo. There are four ways to wire that, and they are NOT interchangeable — the sharp differences are worktree auto-presence, who owns the history, and how you develop/update it. Two classic failures this skill exists to kill: reaching for git submodule by reflex (→ an empty dir in every fresh worktree), and assuming .worktreeinclude will carry a nested repo into a worktree (it will NOT — see leaf 3). Pick from the tree, don't default.

Decision tree — first match wins

  1. Third-party code you'll freeze + slim into THIS trunk (drop heavy media, curate a snapshot)? → vendor-and-commit. This leaf already has a skill: use vendoring-skills for the full procedure (placement, slimming, provenance, the lint B3 allowlist). Don't re-derive it here.
  2. Must be present in every worktree with ZERO setup, and you mainly CONSUME it from upstream (rarely edit it in place)? → subtree.
  3. You DEVELOP it in place as its own repo (push to its own remote), want it in EVERY worktree, and the parent should track only a reviewed source ref? → gitignore + materialize-hook (register it in worktree-repos.json).
  4. Parent must pin an EXACT version and you bump deliberately, and a one-time init step per worktree is acceptable? → submodule.

Discriminators at a glance:

OptionRides into new worktree automatically?History owned byIn-place dev
submoduleNo — empty until git submodule update --initsub-repo (parent pins a SHA)clean
subtreeYes (native tracked files)parent (squashed in)sync ceremony
gitignore + materialize-hookYes (hook clones on EnterWorktree + SessionStart)sub-repo (parent ignores it)clean
vendor-and-commitYes (native tracked files)parent (snapshot)re-vendor only

The four options

submodule

Parent records a gitlink: .gitmodules (URL) + a pinned commit SHA. The sub-repo stays fully independent (own remote, own history).

  • Update: cd <path> && git pull, then in the parent git add <path> && git commit to record the new SHA. Deliberate, never automatic — that pin is the feature (reproducibility).
  • Worktree gotcha: git worktree add does NOT check out submodules (Claude Code uses default git worktree logic per the live docs), so the path is EMPTY in a fresh worktree until git submodule update --init --recursive. Storage is shared via .git/modules, so the init is fast — but it IS a per-worktree step.
  • Pick when: exact-version pinning matters and the init step is acceptable.

subtree

The sub-repo's files become REAL tracked files in the parent — no nested .git, no gitlink.

  • Update: git subtree pull --prefix=<path> <url> <branch> --squash (and git subtree push … to send parent-side edits upstream).
  • Worktree: present natively everywhere, zero setup.
  • Pick when: you consume from upstream and want frictionless worktree presence; you accept subtree-command sync instead of plain git push.

gitignore + materialize-hook ← the way to ride a NESTED REPO into worktrees

The sub-repo stays a LIVE nested repo on disk (its own .git), .gitignored so it's out of the parent's history.

  • .worktreeinclude does NOT work for this. It copies gitignored files, and git does not recurse into a nested-repo boundary, so a nested-repo dir enumerates as nothing to copy. Verified 2026-06-20 (prediction 55b1735b miss — brand-foundry never rode in, twice). Single gitignored files ride fine; a nested repo never does. Do not reach for .worktreeinclude here.
  • Instead, REGISTER it. At the repo root, add a {path, remote, ref} entry to worktree-repos.json, where ref is the reviewed full 40-character commit SHA. hooks/materialize_worktree_repos.py (wired to SessionStart
    • PostToolUse[EnterWorktree]) clones any missing entry into the worktree — from the local primary checkout if present (fast, offline), else the remote, checks out that exact detached commit, verifies it, then points origin at the remote. No-op in the primary checkout; never clobbers an existing dir; fails open. A mutable local experiment may explicitly set development: true, but that mode is not reproducible or distribution-safe and must not ship unnoticed.
  • Update: develop/git pull in place exactly as a standalone repo.
  • Worktree: cloned in automatically on EnterWorktree (verified end-to-end 2026-06-20 — a real EnterWorktree auto-fired the hook and cloned brand-foundry in) and on claude --worktree launch (SessionStart wiring; same engine, not separately live-fired). Subagent isolation:worktree worktrees fire neither trigger — minor edge; clone manually if a subagent needs it.
  • Pick when: you develop it in place as its own repo, want it in EVERY worktree, and the parent should track a reviewed source revision without absorbing the sub-repo's files/history. This is what brand-foundry uses.

vendor-and-commit

Slim a clone and commit a snapshot into the trunk at <path>. → skill vendoring-skills owns this procedure; go there. Native in every worktree; updates = re-vendor (never hand-edit, or you lose upstream-trackability).

Worked example — brand-foundry (this harness, 2026-06-20)

skills/brand-foundry/ is its own repo (github.com/GhostlyGawd/brand-foundry), developed in place; no separate clone exists on disk. Goal: develop in place + ride into every worktree, without the trunk owning its history. → tree node 3: gitignore skills/brand-foundry/ (keeps it out of the trunk) + register its remote and reviewed commit in worktree-repos.json; the materialize hook clones and verifies that commit in each worktree. The FIRST attempt used .worktreeinclude and failed — it can't carry a nested-repo dir (prediction 55b1735b miss). Submodule was rejected (empty in fresh worktrees

  • trunk-coupling); subtree was rejected (would break in-place own-repo dev).

Packaging a MULTI-skill distributable — use the plugin format

The decision tree picks the git-NESTING mechanism; this picks the PACKAGING when the sub-project is a bundle of several skills (plus its own commands/agents) meant to be distributed and invoked as a unit. Package it as a Claude Code plugin, not as skills hand-nested under one skills/<name>/ dir:

  • Give it a .claude-plugin/plugin.json manifest (name is the only field the validator REQUIRES; it only WARNS on missing version/description/author, so add those for a release-quality plugin). Put its skills at <plugin>/skills/<skill>/SKILL.md, and use ${CLAUDE_PLUGIN_ROOT} for any path a component references inside itself. Validate with claude plugin validate <path>.
  • A plugin's skills are invoked namespaced as <plugin>:<skill> (e.g. prospector:validate). The namespacing is mandatory and isolating — a plugin skill is NOT merged into the bare top-level skill namespace. (Verified against the Claude Code plugins reference, 2026-06-23.)
  • To ALSO develop the plugin in place as its own repo and ride it into every worktree, combine this with decision-tree node 3 (gitignore + materialize-hook via worktree-repos.json) — packaging and nesting are orthogonal choices.

(session 16494681, 2026-06-20: placing the 5-skill prospector bundle needed a dedicated claude-code-guide agent to recover the plugin spec from scratch, because no skill covered multi-skill packaging.)

Rules

  • Name the discriminator out loud before picking: worktree auto-presence?, who owns history?, develop-in-place or consume? — those decide it.
  • Don't reach for submodule just because "repo inside a repo" sounds like one; it's the only option that does NOT auto-populate a worktree.
  • Never use .worktreeinclude to carry a NESTED REPO — it's for gitignored files only. Use the registry + materialize hook.
  • This skill is the chooser. Mechanics live elsewhere: vendoring-skills (vendor-and-commit), worktree (.worktreeinclude for gitignored files), and worktree-repos.json + hooks/materialize_worktree_repos.py (nested repos).

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.