Tencent / Tencent/teamai-cli

[bug] Namespace resolution is inconsistent across resource types: push ignores --role/--project for rules and agents, and root-level claudemd never ships

Open
#649 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
4.8k
Forks
342
Avg merge
13h 48m
Merged PRs (30d)
211

Description

Description

Three code paths implement the namespace convention differently. filterRulesByKnowledgeNamespaces is the reference implementation: it matches the first path segment against the active namespace set, and ships any name without a segment to every member.

// src/pull.ts:249-255
if (slashIndex === -1) return true;
const namespace = rule.name.slice(0, slashIndex);
return knowledgeNamespaces.includes(namespace);

Two paths diverge from it. Neither emits a message when it does.

1. push does not apply --role or --project to rules or agents

--project resolves to the project's skills namespace and collapses into options.role (src/push.ts:292-320). Step 4 then applies that namespace to skills alone:

// src/push.ts:799-802
selectedItems.filter(i => i.type === 'skills' && i.status === 'new' && !i.namespace)

A rule pushed with --project front-app is written to rules/<item.name>.md (src/resources/rules.ts:154-155). That path carries no first segment, so the next teamai pull ships the rule to every member of the team.

<repo>/.claude/rules/my-rule.md           project scope scans projectRoot (src/types.ts:1521-1532)
  -> teamai push --project front-app      flag applies to skills only (src/push.ts:799-802)
  -> rules/my-rule.md                     written with no first path segment
  -> teamai pull                          slashIndex === -1 returns true (src/pull.ts:252)
  -> every member of the team

A new agent lands at the agents/ root (src/resources/agents.ts:339-350) and reaches every member through the !agent.namespace || branch (src/pull.ts:302).

The namespace an author needs for a rule is the project's knowledge namespace. ProjectResourceNamespacesSchema permits that namespace to differ from the skills namespace the flag resolved, so the flag targets the wrong directory even where a rule is placed by hand first.

2. collectClaudemdFiles has no root-level branch

Its only reads are path.join(claudemdDir, ns), once per active namespace (src/pull.ts:1543-1553). A file directly under claudemd/ is never opened.

A member with no role receives every namespace, because a null roleContext falls through to listDirs (src/pull.ts:1537-1540). Assigning a role therefore removes content from that member's CLAUDE.md.

Where the convention holds and where it breaks
Resource Namespaced on pull Root ships to everyone --role/--project honoured on push
rules yes (knowledge) yes (src/pull.ts:252) no
agents yes (agents) yes (src/pull.ts:302) no
claudemd yes (knowledge) no not pushable

skills is the only type the flag places, and only for an item that is new and carries no namespace already. pushCore iterates ['skills','rules','env','agents'] (src/push.ts:512-519), so claudemd has no push path at all.

Reproduction

Rules and agents ignoring the flag:

  1. Declare a project front-app in manifest/projects.yaml with a knowledge namespace and a skills namespace.
  2. In the code repository, run teamai projects set front-app. The working directory is now bound to that project in project scope.
  3. Create a rule at .claude/rules/my-rule.md inside that repository. Project scope resolves tool directories against the project root, not against $HOME (src/types.ts:1521-1532).
  4. Run teamai push --project front-app --all.
  5. Open the resulting MR. The rule is at rules/my-rule.md, not under the project's knowledge namespace.

Root-level claudemd not shipping:

  1. Add claudemd/shared.md to the team repo, directly under claudemd/.
  2. As a member with a role, run teamai pull --force.
  3. Read the TEAMAI_CLAUDEMD_START block in the tool's CLAUDE.md. It does not contain the content of shared.md.
  4. Clear the member's role and repeat step 2. The content appears.

Expected behavior

teamai push --project <id> places a rule under the project's knowledge namespace and an agent under its agents namespace. Where a type has no namespace to resolve for the named project, the command fails and names that type, rather than writing to the shared root.

collectClaudemdFiles ships claudemd/*.md to every member, matching the root-level branch that filterRulesByKnowledgeNamespaces already implements.

Suggested fix

Extend the push filter to the two types that already namespace on pull, and resolve each destination from its own axis:

 selectedItems.filter(i =>
-  i.type === 'skills' && i.status === 'new' && !i.namespace
+  (i.type === 'skills' || i.type === 'rules' || i.type === 'agents')
+    && i.status === 'new' && !i.namespace
 )

skills resolves from resources.skills, rules from resources.knowledge, and agents from resources.agents. Where the named project declares no namespace for a selected type, fail and name that type rather than writing to the shared root.

Give collectClaudemdFiles the root branch that filterRulesByKnowledgeNamespaces already has:

 collectClaudemdFiles(claudemdDir, roleContext)
   for ns of activeNamespaces.knowledge
     read path.join(claudemdDir, ns)
+  read the .md files directly under claudemdDir

Environment

Both findings are read from source at main (97a0277, package version 0.22.0). The installed CLI is 0.24.0, the current npm latest.

  • OS: macOS 26.5.2
  • Node.js: v22.22.2
  • teamai: 0.24.0
  • Provider: GitHub
  • AI tool(s): Claude Code

Logs

No --verbose output applies. Both findings come from reading source at the commit above, not from a captured run.

Contributor guide

Open the contributing guide

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.

Research direction

Start with filterRulesByKnowledgeNamespaces and collectClaudemdFiles in src/pull.ts, then trace namespace resolution in src/push.ts and the destination writers in src/resources/rules.ts and src/resources/agents.ts. Reproduce the project-scoped push and root-level claudemd cases described in the issue. Done means rules and agents use their declared project namespaces, missing destinations fail explicitly, and root-level claudemd files ship to every member.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
64/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.