[feat] keep the resources pull delivers in project scope out of git
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 52/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- git, typescript
Research direction
Start with src/mcp-git-exclude.ts and the #886 callers to understand the shared exclude mechanism, then trace pull, uninstall, doctor, and the gitExclude default in src/types.ts and src/init.ts. Verify the delivered paths and self-mode behavior across the mentioned agents. Done means project-scope deliveries are synchronized in .git/info/exclude without hiding tracked or intentionally committed files, while MCP exclusions retain their existing policy.
Written by the indexing model from the issue text.
Description
Problem
In project scope, pull writes team resources into the business repo's working tree, and nothing keeps git away from them. Every member gets them from the team repo on the next pull, so committing them adds nothing. One git add -A still commits them, and the committed copy can then drift from the team repo.
<business repo>/ after `teamai init <team> --agent claude,codex` and `pull` (0.22.0)
├── .claude/skills/demo-skill/SKILL.md ?? untracked, not ignored
├── .claude/skills/teamai/SKILL.md ??
├── .claude/rules/demo-rule.md ??
├── .claude/agents/demo-agent.md ??
├── .claude/CLAUDE.md ?? holds only the teamai block
├── .codex/skills/demo-skill/SKILL.md ??
├── .codex/skills/teamai/SKILL.md ??
├── .codex/rules/demo-rule.md ??
└── .teamai/docs/guide.md ??
#374 set out to leave no teamai residue in the business repo's working tree. It moved the clone, state, index and token to ~/.teamai/projects/<slug>/. The tool folders had to stay, because the agents read them from the repo. The docs mirror stayed too, and #374's R1 (.teamai/docs visibility) was left for a follow-up.
Proposed Solution
When pull delivers a resource into a project scope, add its path to the repo's .git/info/exclude. #882 (PR #886) already does this for project MCP configs that hold resolved tokens, with a marked block that is local to the clone, commits nothing, and is idempotent. This extends the same mechanism to what pull delivers. pull removes a path when it removes the resource, and uninstall removes the block.
pull (project scope, self mode included)
deliver skills, rules, agents, docs
+ sync the teamai block in .git/info/exclude with the delivered paths:
+ .<tool>/skills/<name>/ .<tool>/rules/<file> .<tool>/agents/<file> .teamai/docs/
+ leave out any path git already tracks, and let doctor name it
It is off by default, like sharing.recall.enabled (src/types.ts:84), so existing teams see no change. init turns it on in the teamai.yaml it creates for a new team repo, the way it already writes the other sharing defaults (src/init.ts:1728-1737). A member can override the team's value locally:
# teamai.yaml
sharing:
gitExclude:
enabled: true # written by init for new teams; the default is false; members can override locally
- It works per item, not per folder. A team's own
.claude/settings.json, its repo-specific skills and itsAGENTS.mdstay visible to git. - Files where teamai manages one block next to the team's content stay as they are:
CLAUDE.mdandCODEBUDDY.md,opencode.json(the rulesinstructionsglob), and the MCP configs, which #882 covers. - In self mode the source is
.teamai/, committed on purpose (#198), andinit .also commits the tool settings that carry the hooks. Neither is excluded. The copiespulldelivers from.teamai/into the tool folders are excluded as in project scope:repo.localPathis<repo>/.teamaiandprojectRootis the repo root (src/init.ts:1154-1160), so skills, rules and agents take the same delivery path. Docs need nothing there, because the docs directory is the source andpullcopies nothing (src/resources/docs.ts:307-309). excludehas no effect on a path git already tracks.doctornames such a path and changes nothing.- A member who wants to commit a delivered file on purpose can still run
git add -f. - Files where teamai manages one block stay out even when teamai created them and they hold only its block, as
.claude/CLAUDE.mddoes after a first pull. If the team later adds its own lines there, an excluded file would never be committed, with nothing to warn about it. - One block covers every checkout of the repo, because worktrees read
info/excludefrom the common git dir. I checked that: a linked worktree reports the main repo's path fromgit rev-parse --git-path info/exclude, and a listed path does not show in itsgit status. Checkouts share the project's selection and can differ in which tool folders they have, so the block lists the union, and a line for a path a checkout lacks has no effect. - Hooks need nothing. In project scope teamai writes them to the tool settings in HOME, not to the repo (
docs/usage-guide.md:648,src/hooks.ts:1860). The run above wrote only~/.claude/settings.jsonand~/.codex/hooks.json.
Alternatives Considered
- Exclude whole tool folders (
.claude/,.codex/). It is simpler, but it hides what teams commit there on purpose, and a new file the team adds would never be committed, with nothing to warn about it. - Write the paths into the committed
.gitignore. It changes a file the team owns, on every member's machine, which is the reason #882 gives forinfo/exclude. - On by default for every team. It would silently change what git shows for teams that commit the delivered files on purpose, and their committed copy would stop picking up new resources.
- Off for everyone, with no init default. Nothing changes for anyone, but every team would have to discover the option.
- A second exclude module next to #886's. Two copies of the block, lock and worktree logic would drift apart.
- Keep it to the docs. That is what we have today, and it relies on every member.
Implementation shape
#886 already solves the hard parts in src/mcp-git-exclude.ts: finding info/exclude through the common git dir so worktrees share it, a marked block that never takes the member's lines, a lock and atomic write, a damaged block, an ignore rule that re-includes a path, tracked paths, and --dry-run. Today they sit next to the MCP rule (carriesResolvedValue), and callers combine about a dozen exported helpers themselves.
A small git-exclude module could own that mechanism, with one block per owner and an interface of three calls:
sync(repo, owner, paths) the owner's block holds exactly these paths; tracked paths are left out and reported
remove(repo, owner?) uninstall
report(repo) doctor: listed and tracked paths per owner
flowchart LR
subgraph callers [callers]
pull["pull<br/>skills, rules, agents, docs"]:::added
mcp["mcp-reconcile<br/>(#886)"]:::changed
un[uninstall]:::changed
doc[doctor]:::changed
end
mcprule["mcp-git-exclude.ts<br/>keeps only: which MCP files<br/>carry a resolved value"]:::changed
subgraph ge ["git-exclude.ts: one seam"]
api["sync(repo, owner, paths)<br/>remove(repo, owner?)<br/>report(repo)"]:::added
hidden["hidden inside:<br/>git-path via the common dir<br/>one marked block per owner<br/>lock + atomic write<br/>damaged block, re-including rule<br/>tracked paths, dry run"]:::added
end
file[(".git/info/exclude<br/>block teamai:mcp<br/>block teamai:delivered<br/>member lines untouched")]
pull -- "owner delivered<br/>only if gitExclude.enabled<br/>never under .teamai/ in self mode" --> api
mcp --> mcprule
mcprule -- "owner mcp<br/>always: security" --> api
un -- "remove every owner" --> api
doc -- "report per owner" --> api
api --- hidden
hidden --> file
classDef added fill:#2f8f4f,stroke:#1f6b39,color:#fff;
classDef changed fill:#b5762f,stroke:#865520,color:#fff;
The two owners keep separate policies. The MCP block stays on whatever gitExclude.enabled says, because it protects tokens. The delivered block follows the flag, and in self mode it never lists a path under .teamai/. pull collects the paths from the handlers' desired sets and calls sync once, so each resource type needs no exclude code of its own.
Additional Context
Checked with 0.22.0 in a sandbox: a git repo in project scope with agents claude,codex, and a team repo with one skill, one rule, one agent, one doc and one claudemd/ fragment. The tree above is git status --porcelain -uall after pull. Self mode is checked by reading the code only: in the sandbox, init . rejected the local remote. The implementation needs a real-CLI run of init . and pull in self mode.
Not checked: the paths of the other agents, and Qoder CN and Pi, whose hook code mentions project-level paths (src/hooks.ts:1567, removePiProjectHooks). The implementation needs a real-CLI run for each before it lists their paths.
This builds on the exclude block from #886 (for #882). #886 can land as it is, and the PR for this feature moves its generic part into the shared module, so the token fix does not wait on this. It also helps #913, where a workspace can be a git meta repo that receives its children's resources.
Open questions:
- Should a later major version turn it on by default for every team?
- Dominant language
- TypeScript
- Stars
- 5k
- Forks
- 376
- Avg merge
- 13h 50m
- Merged PRs (30d)
- 306
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Tencent/teamai-cli
-
[feat] contribute --namespace, to file a learning under one of the namespaces a directory readsOpenenhancement
Difficulty 4/5 3-5 days Newbie friendliness 52/100
Tencent/teamai-cli#916 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 25/100
Tencent/teamai-cli#913 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 52/100
Tencent/teamai-cli#912 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 3/5 1-2 days Newbie friendliness 78/100
Tencent/teamai-cli#911 ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
Tencent/teamai-cli#909 ·
Maintainers usually reply within 1 day
All issues in Tencent/teamai-cli
Similar issues
-
refactor
Difficulty 2/5 Half a day Newbie friendliness 84/100
Maintainers usually reply within 5 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
OHDSI/Data2Evidence#3450 ·
Maintainers usually reply within 2 days
-
e2e-failure ready-to-code
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
redhat-developer/rhdh-plugin-export-overlays#4011 · 1 comment ·
Maintainers usually reply within 1 day
-
automation missing-model model-sync provider:ofox
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
anomalyco/models.dev#8421 ·
Maintainers usually reply within 1 day
-
SlackAdapter and TelegramAdapter are not assignable to Adapter under exactOptionalPropertyTypesOpen
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 1 day