Skip to content

fix(git-transport): support path: "." / "" so skills at the repo root are fully fetched#1711

Open
besdar wants to merge 1 commit into
dyoshikawa:mainfrom
besdar:main
Open

fix(git-transport): support path: "." / "" so skills at the repo root are fully fetched#1711
besdar wants to merge 1 commit into
dyoshikawa:mainfrom
besdar:main

Conversation

@besdar
Copy link
Copy Markdown

@besdar besdar commented May 26, 2026

rulesync install with transport: "git" only fetches root-level files when the configured path points at the repository root ("", ".", or "./"). For example, given:

{
  "source": "https://github.com/feature-sliced/skills",
  "path": ".",
  "skills": ["feature-sliced-design"],
  "transport": "git"
}

…only README.md ends up in .rulesync/skills/.curated/feature-sliced-design/, while the actual skill (feature-sliced-design/SKILL.md + the references/ folder) is silently dropped. No error is reported, which makes the failure mode quite confusing — install happily says “Fetched 1 skill(s)”.

Root cause

fetchSkillFiles in src/lib/git-client.ts runs:

git clone --depth 1 --branch <ref> --no-checkout --filter=blob:none -- <url> <tmpDir>
git -C <tmpDir> sparse-checkout set -- <skillsPath>
git -C <tmpDir> checkout

git sparse-checkout in cone mode (the default since git 2.27) treats . / "" / ./ as “top-level files only — exclude every subdirectory”. So after checkout, the working tree contains only the files directly at the repo root, and the subsequent walkDirectory(<tmpDir>/.) only finds those root files.

The downstream groupRemoteFilesBySkillRoot fallback at src/cli/commands/install/install-rulesync.ts (the “rootLevelFiles → singleSkillName” branch) then groups every root-level file under the requested skill name. That’s why the README is written as if it were the body of the skill.

This makes it impossible to install via transport: "git" from any repository that stores skills at the root (<repo>/<skill-name>/SKILL.md without a skills/ container directory). feature-sliced/skills is one such repository — the github transport works fine because it uses the REST listDirectory API which has no equivalent quirk.

Fix

In fetchSkillFiles, treat "", ".", and "./" as “the whole repo”:

  • Call git -C <tmpDir> sparse-checkout disable instead of sparse-checkout set -- ., so the full working tree is restored.
  • Use <tmpDir> itself as the skills root passed to walkDirectory, instead of <tmpDir>/..

For any other path value the behavior is unchanged.

Repro

Before the fix:

$ cat rulesync.jsonc
{
  "sources": [
    {
      "source": "https://github.com/feature-sliced/skills",
      "path": ".",
      "skills": ["feature-sliced-design"],
      "transport": "git"
    }
  ]
}

$ rulesync install
Installing skills from 1 source(s)...
Fetched 1 skill(s) from https://github.com/feature-sliced/skills: feature-sliced-design
Installed 1 skill(s) from 1 source(s).

$ ls .rulesync/skills/.curated/feature-sliced-design
README.md          # ← only this file, no SKILL.md, no references/

After the fix (same config):

$ rulesync install
Installing skills from 1 source(s)...
Fetched 1 skill(s) from https://github.com/feature-sliced/skills: feature-sliced-design
Installed 1 skill(s) from 1 source(s).

$ ls .rulesync/skills/.curated/feature-sliced-design
SKILL.md  references/

$ ls .rulesync/skills/.curated/feature-sliced-design/references
asset-handling.md       excessive-entities.md      layer-structure.md
cross-import-patterns.md framework-integration.md   migration-guide.md
practical-examples.md

Changes

  • src/lib/git-client.ts — branch on skillsPath ∈ {"", ".", "./"}: call sparse-checkout disable and use the clone root directly as the skills root.
  • src/lib/git-client.test.ts — added two test cases:
    1. it.each(["", ".", "./"])("disables sparse-checkout when skillsPath is %j (repo root)", …) — asserts sparse-checkout disable is invoked and sparse-checkout set is not.
    2. "uses the clone directory itself as the skills root when skillsPath is the repo root" — asserts files are read from <tmpDir> directly.

Notes

  • The fallback path for repos like <repo>/SKILL.md (a single root-level skill, no per-skill subdirectory) already exists in groupRemoteFilesBySkillRoot and continues to work; this PR only makes the data reach that fallback when using transport: "git".
  • The github transport (transport: "github", the default) is unaffected — it never had this issue because it lists directories via the REST API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant