From 3dcd2d7e383a9383b1710fec31e5f9d59373a272 Mon Sep 17 00:00:00 2001 From: jeffyxu Date: Mon, 18 May 2026 20:02:25 +0800 Subject: [PATCH] fix(push): exclude .git from copyDir to prevent submodule gitlink in team repo When a skill directory is itself a git repository (e.g. cloned from a remote), copyDir would copy the .git directory into the team repo. Git then treats the nested .git as a gitlink (mode 160000 / submodule reference), causing `git add` to record only a commit hash instead of the actual skill files. The resulting MR appears empty. Add .git to the IGNORED_NAMES set so it is filtered out during copy. Fixes #10 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/__tests__/skills.test.ts | 24 ++++++++++++++++++++++++ src/utils/fs.ts | 1 + 2 files changed, 25 insertions(+) diff --git a/src/__tests__/skills.test.ts b/src/__tests__/skills.test.ts index dceb482..4c21df1 100644 --- a/src/__tests__/skills.test.ts +++ b/src/__tests__/skills.test.ts @@ -654,6 +654,30 @@ scope: 'user', const content = await fse.readFile(contribPath, 'utf-8'); expect(content).toBe('testuser\n'); }); + + it('should NOT copy .git directory when skill source is a git repo', async () => { + const localSkillDir = path.join(homeDir, '.claude/skills', 'git-skill'); + await fse.ensureDir(localSkillDir); + await fse.writeFile(path.join(localSkillDir, 'SKILL.md'), '# Git Skill\nContent here'); + await fse.writeFile(path.join(localSkillDir, 'helper.py'), 'print("hello")'); + // Simulate a .git directory (as if skill was cloned from a git repo) + await fse.ensureDir(path.join(localSkillDir, '.git', 'objects')); + await fse.writeFile(path.join(localSkillDir, '.git', 'HEAD'), 'ref: refs/heads/main'); + + const item = { + name: 'git-skill', + type: 'skills' as const, + sourcePath: localSkillDir, + relativePath: 'skills/git-skill', + }; + + await handler.pushItem(item, teamConfig, localConfig); + + const destDir = path.join(localConfig.repo.localPath, 'skills', 'git-skill'); + expect(await fse.pathExists(path.join(destDir, 'SKILL.md'))).toBe(true); + expect(await fse.pathExists(path.join(destDir, 'helper.py'))).toBe(true); + expect(await fse.pathExists(path.join(destDir, '.git'))).toBe(false); + }); }); describe('SkillsHandler.readContributors', () => { diff --git a/src/utils/fs.ts b/src/utils/fs.ts index 6243f08..ff44fac 100644 --- a/src/utils/fs.ts +++ b/src/utils/fs.ts @@ -8,6 +8,7 @@ const IGNORED_NAMES = new Set([ '.pyc', '.DS_Store', 'node_modules', + '.git', ]); function isIgnored(name: string): boolean {