Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[feat] keep the resources pull delivers in project scope out of git

Open
#915 0 comments 0 reactions 0 assignees View on GitHub

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
Domain
cli, devtools

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

enhancement

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 its AGENTS.md stay visible to git.
  • Files where teamai manages one block next to the team's content stay as they are: CLAUDE.md and CODEBUDDY.md, opencode.json (the rules instructions glob), and the MCP configs, which #882 covers.
  • In self mode the source is .teamai/, committed on purpose (#198), and init . also commits the tool settings that carry the hooks. Neither is excluded. The copies pull delivers from .teamai/ into the tool folders are excluded as in project scope: repo.localPath is <repo>/.teamai and projectRoot is 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 and pull copies nothing (src/resources/docs.ts:307-309).
  • exclude has no effect on a path git already tracks. doctor names 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.md does 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/exclude from the common git dir. I checked that: a linked worktree reports the main repo's path from git rev-parse --git-path info/exclude, and a listed path does not show in its git 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.json and ~/.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 for info/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

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Tencent/teamai-cli

All issues in Tencent/teamai-cli

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.