Skip to content

fix(locate): every command finds a tool where pull wrote it, also when its home moved (#1221) - #1486

Merged
SaulMoro merged 41 commits into
Tencent:mainfrom
hz89Dev:saulmoro/1221-locate-callers
Oct 28, 2026
Merged

SaulMoro merged 41 commits into
Tencent:mainfrom
hz89Dev:saulmoro/1221-locate-callers

Conversation

@hz89Dev

@hz89Dev hz89Dev commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

When a member moves a tool's home with an environment variable, pull wrote to the moved home, but skill list, doctor's install checks, uninstall, hook writers and the local agent still looked in the default one, each through its own resolver. Now every caller asks one resolver, locate(tool, resource, install, host), and the per-tool resolvers are deleted. What was still hand-coded per tool is modeled in TOOLS.

 callers (skills list/status, doctor install checks, uninstall, hooks, rules, local agent, MCP)
-  resolveToolBaseDir, getCopilotHome, getHermesHome, resolveDshHome,
-  resolveOpenclawWorkspaceDir, detectMcpFormat, isToolInstalledForConfig
+  locate(tool, resource, install, host), isInstalledFor, toolHome

 TOOLS (src/tools.ts)
+  hooks.workspace            Copilot's .github/hooks/teamai.json
+  agents                     Trae: .trae/agents (project), Trae CN: also ~/.trae-cn/agents
   HomeMove kinds
-  profile-suffix, win32-appdata
+  openclaw-state, omp-agent, hermes-home

Homes now follow each tool's own rules, read from upstream source or docs:

Tool Before After
OpenClaw (and variants) OPENCLAW_STATE_DIR, profile suffix OPENCLAW_STATE_DIR wins, else OPENCLAW_HOME composed with OPENCLAW_PROFILE
Oh My Pi ~/.omp/agent, whatever the env PI_CONFIG_DIR, OMP_PROFILE (or PI_PROFILE). PI_CODING_AGENT_DIR stays Pi's: teamai does not follow it for Oh My Pi. models.yml follows the same home
Hermes HERMES_HOME HERMES_HOME, HERMES_DATA_DIR_SUFFIX or Windows LOCALAPPDATA name the home tool-homes.json records. The profile active_profile names is applied inside that home on every run and never recorded, so hermes profile use takes effect at the next pull, from any shell

Fixes found on the way:

Area Before After
OpenClaw variants (QClaw, EasyClaw, AutoClaw) Their hook injection rewrote OpenClaw's handler Each writes only its own
Source skill in a moved home Second pull failed: Invalid source ownership record Stays managed
Copilot uninstall Left an emptied hooks/teamai.json Deletes the file teamai emptied; keeps one git tracks
uninstall --agent <tool without settings file> Removed OpenClaw's workspace hook Only OpenClaw's uninstall removes it
uninstall for Hermes and Oh My Pi Cleaned one home: after a move or a profile switch, the hook, plugin, config.yaml entries and SOUL.md block (Hermes) or the extension (Oh My Pi) stayed in the other; with HERMES_HOME recorded at init, uninstall from a shell without it looked in ~/.hermes Cleans the default, recorded and moved homes and every profile under them, by teamai's marker or exact command, resolved before ~/.teamai is removed. pull and hooks remove still touch only the active profile
Trae agents Not synced Synced as Trae's Markdown, tools as one string (tools: '' for none, since Trae gives every tool to an agent without the field). A name Trae ignores is skipped with a warning
Trae and Trae CN in one project (new) They read one .trae/agents file, so it gets one render: no edition model, both editions' other tool_extras. A key the two give different values skips the agent for Trae, with a warning; push refuses an edit that changes or removes a key the other edition gives, and keeps an edition model the file leaves out, whatever else changes. User scope keeps tool_extras.trae-cn.model
doctor agents (new) Names each agent a tool it targets would not load, and why, instead of reporting it unreachable or passing
doctor read notes Changed with the directory doctor ran from Same from every directory; a project install is judged for any session in the project. Severities and exit codes unchanged
Oh My Pi profiles (new) A pull under OMP_PROFILE=work leaves the default profile's extension in place, since profiles run side by side

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature causing existing behavior to change)
  • Documentation only
  • Refactor / internal cleanup

Test Plan

  • npx tsc --noEmit passes
  • npm run lint passes
  • npx vitest run passes: at db61733 (on e0d17c4), 18529 passed, 22 skipped, 2 failed: workflow-local-driver and workflow-settle-cost are timing tests that miss under full-suite load; both files pass alone (156/156), and this PR does not touch workflows. The run before (53b051c) had 0 failures.
  • Added/updated tests for the change: each fix has a test seen failing first, through locate, nextToolHomes/toolHome, reconcileHooksToAllTools, uninstall, AgentsHandler.pullItem/scanLocalForPush and doctor's checks from several directories. The locate matrix gate fails when a home move has no matrix row.

Real CLI (node dist/index.js, throwaway HOME, local bare git team repo, --provider git), at HEAD:

Hermes, HERMES_HOME recorded while the work profile is active, then a switch back to default:

$ HERMES_HOME=<SB>/hermes-home teamai init <url> --provider git --agent hermes --scope user --force   (active_profile=work)
ℹ Recorded HERMES_HOME as the hermes home: <SB>/hermes-home. teamai syncs hermes there from any shell until `teamai init` records another
✔ Injected teamai Hermes hook into <SB>/hermes-home/profiles/work/hooks/teamai-status-report.sh
✔ teamai initialized successfully!
init exit=0
$ teamai pull   (no HERMES_HOME in this shell)
- [user] Pulling team repo...
✔ [user] Team repo: already up to date
✔ [user] Synced 1 skills (1 new, 0 updated)
    new: hello
pull exit=0
-- SKILL.md files under <SB>/hermes-home
<SB>/hermes-home/profiles/work/skills/hello/SKILL.md
<SB>/hermes-home/profiles/work/skills/teamai/SKILL.md
-- ~/.teamai/tool-homes.json
{
  "hermes": "<SB>/hermes-home"
}

$ printf 'default\\n' > <SB>/hermes-home/active_profile; teamai pull   (no HERMES_HOME)
- [user] Pulling team repo...
✔ [user] Team repo: already up to date
✔ [user] Synced 1 skills (1 new, 0 updated)
    new: hello
ℹ [user] hermes takes skills from <SB>/hermes-home/skills now, so teamai no longer updates its copies in <SB>/hermes-home/profiles/work/skills. Delete them once no tool reads that directory.
ℹ [user] hermes takes the team rules from <SB>/hermes-home/SOUL.md now, so teamai no longer updates them in <SB>/hermes-home/profiles/work/SOUL.md. Delete them there once no tool reads it.
ℹ [user] hermes takes the instruction blocks from <SB>/hermes-home/SOUL.md now, so teamai no longer updates them in <SB>/hermes-home/profiles/work/SOUL.md. Delete them there once no tool reads it.
✔ Injected teamai Hermes hook into <SB>/hermes-home/hooks/teamai-status-report.sh
pull exit=0
-- SKILL.md files under <SB>/hermes-home
<SB>/hermes-home/profiles/work/skills/hello/SKILL.md
<SB>/hermes-home/profiles/work/skills/teamai/SKILL.md
<SB>/hermes-home/skills/hello/SKILL.md
<SB>/hermes-home/skills/teamai/SKILL.md
-- ~/.teamai/tool-homes.json
{
  "hermes": "<SB>/hermes-home"
}

Trae and Trae CN in one project:

$ teamai init <url> --provider git --agent trae --agent trae-cn --scope project --force
init exit=0
$ teamai pull
ℹ workspace scope detected, skipped user scope
✔ [workspace] Synced 1 agents
pull exit=0
-- <proj>/.trae/agents/reviewer.md
---
name: reviewer
description: Review team changes
tools: ''
disallowedTools: Bash
---
Review the requested change and report defects.
== the team gives trae a different disallowedTools
$ teamai pull
ℹ workspace scope detected, skipped user scope
⚠ [agents] Skipped reviewer for trae: Trae and Trae CN read one project file, and tool_extras.trae.disallowedTools and tool_extras.trae-cn.disallowedTools differ. Give both editions the same value, or set it for one edition only.
⚠ [agents] Skipped reviewer for trae-cn: Trae and Trae CN read one project file, and tool_extras.trae.disallowedTools and tool_extras.trae-cn.disallowedTools differ. Give both editions the same value, or set it for one edition only.
✔ [workspace] Synced 1 agents
pull exit=0
$ teamai doctor   (agent lines)
  ✔ trae agents are where the last pull wrote them
  ✔ trae-cn agents are where the last pull wrote them
  ✖ Every team agent loads in the tools it targets
    → Pull gives these agents no copy, since the tool would not load them. reviewer for trae: Trae and Trae CN read one project file, and tool_extras.trae.disallowedTools and tool_extras.trae-cn.disallowedTools differ. Give both editions the same value, or set it for one edition only. reviewer for trae-cn: (same) Fix the agent in the team repo.
  ✔ No team instruction blocks are left in files no tool loads them from
doctor exit=1

Earlier real-CLI runs on this branch, same setup:

  • Muse with XDG_CONFIG_HOME: pull writes $XDG_CONFIG_HOME/muse/skills, teamai list skills --source local lists that dir with 2 skills, doctor exits 0 with all Muse checks ✔.
  • Copilot with COPILOT_HOME: pull and uninstall --force work only in $COPILOT_HOME; uninstall prints Removed <copilot-home>/hooks/teamai.json: no hooks left in it.
  • Trae CN: pull writes ~/.trae-cn/agents/reviewer.md (user) and <proj>/.trae/agents/reviewer.md (project) with tools: 'Read, Grep, Glob'.
  • OPENCLAW_HOME=<oc-home>: init records <oc-home>/.openclaw, the hook and skills land in <oc-home>/.openclaw/workspace.
  • PI_CODING_AGENT_DIR with Pi and Oh My Pi: Pi's extension in that dir, OMP's in ~/.omp/agent, both unchanged across three pulls including --force.
  • OMP_PROFILE=work teamai models switch local:tokenhub --agent omp writes ~/.omp/profiles/work/agent/models.yml.
  • Source skill in a moved OpenCode home: second pull Synced 1 skills (all updated), previously Invalid source ownership record.
  • Project doctor from the root and from src/: identical output.
  • uninstall --force after (a) HERMES_HOME recorded at init, run without it, (b) Hermes pulled under default then work, (c) Oh My Pi pulled without a profile then with OMP_PROFILE=work: no teamai file is left in any home; config.yaml and the allowlist keep no teamai entry.

Not run with a real binary of the tool: OpenClaw, Oh My Pi, Hermes, Trae loading what teamai wrote. Only git provider and user/project scope on macOS; other providers and Windows are left to CI.

Related Issues

Continues #1221 (item 2 of "Done when").

Notes for Reviewers

For an unmoved home, no path teamai reads or writes changes; the only differences are the fixes listed above.

Not moved or not done here:

Item Why
claude-hook-host.ts Reads COPILOT_HOME in a generated shell snippet and calls none of the deleted resolvers
~ in OPENCLAW_CONFIG_PATH / OPENCLAW_WORKSPACE_DIR Upstream expands it against OPENCLAW_HOME (config) or not at all (workspace); unchanged here
AutoClaw paths No upstream source found
Trae "Enable Subagents Directory" switch Where Trae stores it is unknown, so it is a docs note, not a read gate
Per-tool string tables satisfies ToolId[] catches typos only; 15 of 32 tables stay untyped. Exhaustiveness needs each table moved into TOOLS
Hermes active_profile on Windows No locate matrix row
WorkBuddy 5.7 paths, trust gates (#1221 item 4), #1419 Out of scope
A Trae copy pull wrote before its agent became skipped Stays on disk; doctor names the skip

Rolling back is a revert: tool-homes.json keeps the same shape (a directory per tool), so an older release reads what this one records.

@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Preserve edition model pins when pushing project agents — src/resources/agents.ts:1464. Given tool_extras.trae-cn.model: kimi-k2.5 and no root model alias, pull into a project, edit only the agent’s instructions, then push. The scoped render omits model, but mergeCanonicalEdits still compares it as a model pin: the baseline contains { model: undefined } while the edited extras contain no key. It consequently removes the edition’s model pin from the merged team YAML, unintentionally changing subsequent user-scope deployments. Preserve intentionally unrendered model pins and add a push regression test.

The PR description documents sufficient testing, including representative real-CLI runs at HEAD. I did not run, build, or install PR code.

@hz89Dev

hz89Dev commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in ed17dd6. mergeCanonicalEdits now treats tool_extras.<tool>.model as a pin only when the deployed copy carries a model (baseline.spec.model !== undefined). A Trae project file carries none, so the pin is no longer compared and stays in the team YAML. User scope and tclaude/tcodex are unchanged.

Regression test: trae-agents.test.ts "keeps an edition model the project file leaves out when only the instructions change". With tool_extras.trae-cn.model: kimi-k2.5, it pulls into a project, edits only the instructions and scans for push. Before the fix, mergedSpec.tool_extras came back undefined; it now keeps { 'trae-cn': { model: 'kimi-k2.5' } }.

@hz89Dev
hz89Dev force-pushed the saulmoro/1221-locate-callers branch from ed17dd6 to 5e9f32c Compare October 27, 2026 23:20
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Keep omitted model pins when another extra changes — src/resources/agents.ts:1481. With tool_extras.trae-cn: { model: kimi-k2.5, disallowedTools: Bash } and no root model, pull into a project, change only disallowedTools to Write, then push. The project render omits model, so modelPin is false. The extras merge then replaces the edition’s entire extras object from after, silently deleting its model pin and changing subsequent user-scope deployments. Preserve the unrendered pin when rebuilding extras and add a regression test.

The earlier instructions-only reproduction is resolved and covered by a regression test; the extras-edit scenario above remains.

The PR description documents sufficient testing, including representative real-CLI runs at HEAD. I did not run, build, or install PR code.

@hz89Dev

hz89Dev commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in b9da8f5. When push rebuilds tool_extras.<tool> from a copy that carries no model (baseline.spec.model === undefined, as in a Trae project file), it now keeps the existing model pin. The copy cannot have edited it.

Regression test: trae-agents.test.ts "keeps an edition model the project file leaves out when another of its extras changes". It starts from tool_extras.trae-cn: { model: kimi-k2.5, disallowedTools: Bash }, changes disallowedTools to Write in the project file and scans for push. Before the fix the model was dropped; mergedSpec.tool_extras is now { 'trae-cn': { model: 'kimi-k2.5', disallowedTools: 'Write' } }.

@github-actions

Copy link
Copy Markdown

No new findings in the scoped PR diff.

Both earlier model-pin-loss findings are resolved: omitted pins are preserved for instructions-only edits and when another extra changes. Regression tests cover both scenarios.

The PR description documents sufficient testing, including representative real-CLI runs at HEAD. Additional provider/platform coverage is explicitly deferred to CI.

I did not run, build, or install PR code.

@SaulMoro SaulMoro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

Reviewed at b9da8f5. Both CI review findings on the Trae CN model pin are fixed. With the built CLI, an instructions-only edit and a disallowedTools edit to the project file both keep tool_extras.trae-cn.model after push. Each regression test fails on the commit before its fix. skill list now finds a moved Hermes home, which main misses. doctor output, severities and exit code are the same from the project root and a subdirectory.

Blocking

  • [P1 blocking] After a home move this PR introduces, uninstall cleans only the new home.
    • Hermes. Main installs into ~/.hermes while ~/.hermes/active_profile is work. This build's pull moves everything to profiles/work, then uninstall --force cleans profiles/work. What stays in ~/.hermes:
      • hooks/teamai-status-report.sh;
      • plugins/teamai-instructions/*;
      • the config.yaml entries (hooks.on_session_start, plugins.enabled: [teamai-instructions]) and the allowlist approval;
      • the SOUL.md team-rules block.
    • Oh My Pi. Main installs ~/.omp/agent/extensions/teamai-hooks.ts. After OMP_PROFILE=work teamai pull, uninstall --force removes only the profile's extension. ~/.omp/agent, which the comment in src/tools.ts calls the live default profile, keeps loading it.
    • Main's uninstall removes all of these.
    • pull's "no longer updates" notes name the skills, rules and blocks left behind, but not the hook, extension or plugin. doctor stays green.
    • The same happens without an upgrade, for a member who switches profile and later uninstalls.
    • Suggested fix: have uninstall also clean the earlier homes' hook and adapter files that teamai recognises by marker, as earlierHookAdapterDir already does for OMP's pre-#1221 extension in reconcileOmpExtension, and name those files in pull's notes.
  • [P1 blocking] With HERMES_HOME recorded at init, uninstall from a shell without it leaves the Hermes hook and plugin.
    • Scenario: HERMES_HOME=<dir> teamai init … --agent hermes --scope user, then pull and uninstall --force without the variable.
    • Skills are removed, but <dir>/hooks/teamai-status-report.sh, plugins/teamai-instructions/*, the config.yaml entries and the approval stay.
    • Cause: executeRemoval deletes ~/.teamai, and tool-homes.json with it (src/uninstall.ts:2426), before removeHermesHooks() runs (:2471). That call then resolves toolHome('hermes') to ~/.hermes.
    • The PR already moved the SOUL.md path into the plan (hermesSoul) to survive this ordering. The hook, plugin and config paths need the same treatment, or the Hermes step needs to run before ~/.teamai is removed.
    • Main has this too, but the PR promises that uninstall finds a tool where pull wrote it. Copilot and OpenClaw work in the same scenario.

Non-blocking

  • [P3 nit] The homes table lists Oh My Pi "Before: PI_CODING_AGENT_DIR". At the merge base OMP's home did not move (omp: { home: { dir: '.omp' } }), so a member with PI_CODING_AGENT_DIR set sees no change. Correcting the Before cell keeps a changelog from announcing a removal.

Checks: tsc and lint exit 0. The changed unit files plus the README-matrix, tool-support and command-reference tests pass (36 files, 5118 tests), also with a symlinked TMPDIR. 19 of the 21 new tests fail on the base. The PR's e2e file passes 8/8.

Not checked: real Hermes, Oh My Pi, OpenClaw and Trae binaries; Windows beyond CI; the gitlab/github providers, left to CI. The OpenClaw variants' hook isolation and uninstall --agent <tool without settings file> are covered by unit tests only.

This now conflicts with #1474 and #1477 in src/__tests__/doctor.test.ts, and with #1414 in src/resources/agents.ts and src/uninstall.ts.

@SaulMoro
SaulMoro self-requested a review October 27, 2026 23:39
…cent#1221)

A team repo's workspace keeps Copilot's team hooks in the project's
.github/hooks/teamai.json and the built-ins in the user file. TOOLS now
declares that file as hooks.workspace, locate answers it beside the user
file, and pull, hooks list and remove, uninstall and doctor read both
from locate instead of joining the project root or Copilot's home by
hand. The same files are written, read and removed.
Both editions load Markdown subagents from a project's .trae/agents, and
Trae CN also from ~/.trae-cn/agents (Trae 3.5.67, Trae CN 3.3.67). Where
international Trae reads user agents is not established: its docs give
Trae CN's path, so user scope stays unknown and teamai writes none there.

Trae takes tools as one comma-separated string and a model only from its
own ids, which differ per edition while both read the same project
directory. The render leaves out the agent's own model and writes one an
alias or tool_extras gives the edition. Loading also needs Trae's Beta
"Enable Subagents Directory" switch, which teamai cannot read; the
descriptor and the sharing guide say so.
…1221)

The agent spec's tool_extras keys come from the tools teamai renders
agents for, and the Codex-family lists in the auto-compact and co-author
reconcilers use CODEX_TOOL_IDS instead of restating it. The capability
allow-lists outside TOOLS (shell-dependent hooks, SubagentStop, Stop
stdout, project MCP judging, preapproving skills, search evidence,
bundled shells, agent type aliases, main-checkout team hooks, the
single-repo tool choices) are checked against ToolId, so a renamed or
misspelled tool fails tsc instead of silently dropping out.
…encent#1221)

Each resource matrix now checks that every home move TOOLS declares
(an env variable, XDG_CONFIG_HOME, %LOCALAPPDATA% or a profile suffix)
which moves a write of that resource has a row setting it, so a new move
or a new channel under a moving home fails until its cell is written.

The cells it found missing are added from each tool's source: OpenClaw's
state dir and profile for its rules, instruction blocks, project skills
and hooks, CodeBuddy's CODEBUDDY_CONFIG_DIR for user skills, rules and
blocks and project sessions, Hermes' HERMES_HOME and %LOCALAPPDATA% for
its SOUL.md and project hooks, and the workspace's user hooks under Pi,
OpenCode and Muse home moves. OpenClaw's OPENCLAW_WORKSPACE_DIR and
OPENCLAW_CONFIG_PATH, which TOOLS does not record as home moves, get
rules, blocks and hooks cells by hand.
…1221)

Removing teamai's hooks keeps the events it empties, so uninstall left
$COPILOT_HOME/hooks/teamai.json holding only empty arrays. Copilot runs
nothing from that file, and the file is teamai's: uninstall now deletes
it once nothing else is left in it and git does not track it. A file
still holding a member's hook stays.
…wn rules (Tencent#1221)

OPENCLAW_HOME replaces the OS home for OpenClaw's state dir and composes
with OPENCLAW_PROFILE. Oh My Pi's agent dir follows OMP_PROFILE, PI_PROFILE,
PI_CONFIG_DIR and, for the default profile, PI_CODING_AGENT_DIR, which its
task agents do not follow. Hermes's home takes HERMES_DATA_DIR_SUFFIX and
the profile active_profile names.
…er (Tencent#1221)

injectHooksToAllTools sent qclaw, easyclaw and autoclaw to the OpenClaw
adapter, which writes into OpenClaw's own workspace, so the last variant
in toolPaths left OpenClaw dispatching under its name. Inject now routes
only openclaw there, as reconcile already does.
…ncent#1221)

A source destination outside the scope root had to lie in a hand list of
homes (Hermes, OpenClaw, Copilot), so a skill pulled into a home that
XDG_CONFIG_HOME, PI_CODING_AGENT_DIR or CODEBUDDY_CONFIG_DIR moves outside
HOME made the next pull reject the installation record. The list is now
every tool's home and OpenClaw's workspace.
… in (Tencent#1221)

doctor judged each read from its own working directory, so its notes
changed with where it ran. A project install's reads are now judged from a
session at the project root, and a user install's from any session, so a
read limited to sessions under one directory is always named.
…me (Tencent#1221)

Trae loads a subagent only when its name starts with a letter, holds only
letters, digits and -, ends with a letter or digit, and is at most 50
characters. Pull wrote such an agent anyway and Trae dropped it in
silence; it now skips it for trae and trae-cn and says why, while other
tools still receive it.
…r profile pulls (Tencent#1221)

OMP profiles run side by side, so ~/.omp/agent is the live default profile, not a leftover. Pull, hooks remove and uninstall no longer delete its extension or name its MCP servers as left behind.
…encent#1221)

Every tool without a settings file looked in OpenClaw's workspace hooks, so uninstalling Hermes or Trae deleted OpenClaw's handler and left its openclaw.json entry enabled. Only OpenClaw and its variants look for the HOOK.md dir now, and only OpenClaw in its workspace.
…ent#1221)

Both editions read a project's .trae/agents, so a tool_extras model for one edition landed in the other's file and the last writer won. A project copy now never carries a model; Trae CN's user copy in ~/.trae-cn/agents still takes its edition's model. Pull, push and copy proofs render with the scope they compare in.
…encent#1221)

A pull on Windows records %LOCALAPPDATA%\hermes, and init records
HERMES_HOME. The record then won over active_profile, so after
`hermes profile use work` teamai kept writing to the default profile.
The record now holds Hermes's home only, and the profile active_profile
names is applied on top of it on every run. Init also records HERMES_HOME
again while a profile is active.
…#1221)

Trae gives every tool to an agent with no tools field, and none to one with tools: ''. An empty tools list was dropped from the render, so it granted every tool.
…encent#1221)

Each edition rendered its own tool_extras into the shared .trae/agents file, so one edition's restriction depended on which pull wrote last and the other edition's delivery check failed. The project file now takes both editions' tool_extras but model. An agent whose editions give one key different values is skipped for Trae there, with a warning naming the key.
… file (Tencent#1221)

A project's Trae copy renders the other edition's tool_extras, so push treats those keys as inherited, as it does for tclaude and tcodex: removing one is refused, and an edit that would give the two editions different values is refused too. A copy pull skips for a tool is no longer a delivery doctor checks.
… keeps its old copies' rules (Tencent#1221)

A tool that would not load an agent as teamai renders it (a name Trae ignores, or a Trae project key the two editions give different values) is left out of pull's and doctor's delivery set, and doctor now names each such agent, tool and reason instead of reporting it unreachable or saying nothing. Namespace cleanup still sees the agent's render, so a skipped replacement keeps the root copy it replaces and an inactive copy is removed as before.
…ut (Tencent#1221)

A Trae project copy carries no edition model, so push compared the pin in tool_extras.<edition>.model against nothing and dropped it from the team file on any other edit. A pin the copy does not carry is no longer compared.
…as changes (Tencent#1221)

Rebuilding tool_extras.<edition> from a Trae project copy dropped its model, which the copy never carries. A model pin the copy does not carry is now kept.
…recorded (Tencent#1221)

Removing ~/.teamai took the tool-homes record with it before the Hermes
step ran, so that step looked in ~/.hermes and left the hook script,
the plugin, and their config.yaml entries and approval in the recorded
home. The plan now names the home, and the Hermes removers take it.
…home and profile (Tencent#1221)

Pull writes only the active Hermes or Oh My Pi profile, so a pull before
a home move or a profile switch leaves teamai's hook script, plugin,
config.yaml entries and SOUL.md block, or its Oh My Pi extension, in
another home. Uninstall cleaned only the active one. It now looks in the
default, recorded and moved homes and every profile under them, removes
what carries teamai's marker or exact command, and lists each in its
summary. Pull and hooks remove still touch only the active profile.
@hz89Dev
hz89Dev force-pushed the saulmoro/1221-locate-callers branch from b5b3831 to fb14f46 Compare October 28, 2026 00:16
@hz89Dev

hz89Dev commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Both blocking findings are fixed. The branch is rebased on e0d17c4.

  • uninstall after a home move or a profile switch: fb14f46. Uninstall now looks in Hermes's and Oh My Pi's default, recorded and env-moved homes and in every profiles/* under them (toolHomesAcrossProfiles in src/tool-home.ts). In each one it removes teamai's hook script, instructions plugin, config.yaml entries, allowlist approval and SOUL.md block (Hermes), or its extension (Oh My Pi). It removes only what carries teamai's marker or exact command, and the plan lists every file. pull and hooks remove still touch only the active profile, since profiles run side by side.
  • HERMES_HOME recorded at init, uninstall from a shell without it: 5b7435a. The Hermes home is resolved while the plan is built, before ~/.teamai and tool-homes.json are removed, and passed to the Hermes removers. No other tool's uninstall step resolves its home after ~/.teamai is gone.

Each finding has a test through uninstall(...) in uninstall.test.ts that failed before its fix. Real CLI against dist/:

  • (a) Your HERMES_HOME scenario.
  • (b) Hermes pulled under default, then work, then uninstalled.
  • (c) Oh My Pi pulled without a profile, then with OMP_PROFILE=work, then uninstalled.

None of the three leaves a teamai file in any home, and config.yaml and the allowlist keep no teamai entry.

Not changed: pull's moved-home notes still name only skills, rules and blocks. Naming the hook, extension or plugin there needs a new record field that remembers each tool's hook files per pull, and it still could not name what a release before this PR wrote. Uninstall now covers both cases.

The P3 is fixed in the body: Oh My Pi's "Before" is ~/.omp/agent whatever the env.

@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Scan sibling profiles under OMP’s recorded configuration root — src/tool-home.ts:214. Initialize OMP with PI_CONFIG_DIR=.omp-alt in the default profile, then reinitialize with OMP_PROFILE=work, leaving extensions in both profiles and recording $HOME/.omp-alt/profiles/work/agent. Run uninstall --force without either variable: this loop scans only $HOME/.omp, while moves adds just the recorded agent directory. The default profile’s .omp-alt/agent/extensions/teamai-hooks.ts survives uninstall. Derive the configuration root from the recorded home and scan its default agent directory and sibling profiles; add a regression test.

Both earlier model-pin-loss findings are resolved, with regression tests covering instructions-only and extras edits.

The PR description documents sufficient testing, including representative real-CLI runs at HEAD. Additional provider/platform coverage is explicitly deferred to CI.

I did not run, build, or install PR code.

…rded home (Tencent#1221)

A home recorded under PI_CONFIG_DIR left that root's default profile and other profiles unscanned when uninstall ran from a shell without PI_CONFIG_DIR, so their extension stayed.
@hz89Dev

hz89Dev commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 30914dc. toolHomesAcrossProfiles now derives each recorded or moved Oh My Pi home's root (<root>/agent or <root>/profiles/<name>/agent), and scans that root's default agent dir and its sibling profiles too.

Regression test: uninstall.test.ts "removes teamai's Oh My Pi extension from the default profile of the PI_CONFIG_DIR init recorded, from a shell without it". It follows your scenario: extensions in .omp-alt/agent and .omp-alt/profiles/work/agent, .omp-alt/profiles/work/agent recorded, then uninstall --force without either variable. It failed before the fix on the default profile's extension, and both are removed now.

@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Scan sibling profiles when Hermes records a profile directory — src/tool-home.ts:208. Initialize user scope with HERMES_HOME=/srv/hermes, then reinitialize with HERMES_HOME=/srv/hermes/profiles/work. Running uninstall --force without that variable scans ~/.hermes and the recorded profile’s own profiles/ directory, never /srv/hermes. The default home’s teamai hook and plugin survive uninstall. Derive the configuration root from a recorded profiles/<name> home and scan its default and sibling profiles; add a regression test.

All three earlier findings are resolved: both Trae model-pin-loss cases and OMP’s recorded-root cleanup have fixes and regression tests.

The PR description documents sufficient testing, including representative real-CLI runs at HEAD. Additional provider/platform coverage is explicitly deferred to CI.

I did not run, build, or install PR code.

… profile home (Tencent#1221)

A HERMES_HOME recorded as <root>/profiles/<name> left <root> and its other profiles unscanned when uninstall ran from a shell without HERMES_HOME, so the hook and plugin there stayed.
@hz89Dev

hz89Dev commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 53b051c. When a recorded or moved Hermes home is <root>/profiles/<name>, toolHomesAcrossProfiles now also scans <root> and every profile under it.

Regression test: uninstall.test.ts "removes the Hermes hook and plugin from the root of a recorded profile home, from a shell without HERMES_HOME". It follows your scenario: hooks injected under HERMES_HOME=<root> and then <root>/profiles/work, the profile recorded, and uninstall --force run without the variable. It failed before the fix on <root>'s hook, and both homes are clean now.

@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Preserve shared Trae agents during targeted uninstall — src/types.ts:581. Pull a project agent with both trae and trae-cn enabled, then run teamai uninstall --agent trae --force. Both editions use .trae/agents, but uninstall appends the selected tool’s agent files without checking whether another enabled tool still needs them (src/uninstall.ts:1404). It deletes reviewer.md while Trae CN remains configured. Retain shared agent files and add a targeted-uninstall regression test.

All four earlier findings are resolved, with regression tests.

The PR description documents sufficient testing, including representative real-CLI runs at HEAD. Additional provider/platform coverage is explicitly deferred to CI.

I did not run, build, or install PR code.

…encent#1221)

Trae and Trae CN share a project's .trae/agents, so uninstall --agent for one deleted the agent files the other still reads. They stay now, as shared skill directories do.
@hz89Dev

hz89Dev commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in db61733. uninstall --agent <tool> now keeps an agent file that another enabled, installed tool's resources also name, the same retention shared skill directories already get (#904). It counts toward the "kept for the other tool" outcome, so the exclusion is still recorded.

Regression tests in uninstall.test.ts, under "Trae and Trae CN share a project's .trae/skills":

  • "uninstall --agent %s keeps the shared project agents the other still reads", for trae and trae-cn. Both failed before the fix.
  • "a full uninstall removes the shared project agents", which shows the file is recognised as teamai's.

@github-actions

Copy link
Copy Markdown

No new findings in the scoped PR diff.

All five earlier findings are resolved, with regression tests: both Trae model-pin-loss cases, OMP and Hermes recorded-root cleanup, and shared Trae agents during targeted uninstall.

The PR description documents sufficient testing, including representative real-CLI runs at HEAD. Additional provider/platform coverage is explicitly deferred to CI.

I did not run, build, or install PR code.

@SaulMoro SaulMoro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

Approving at db61733. Everything from our review and from each CI review since is fixed. Each case was reproduced on the commit before its fix and checked with the built CLI after it:

  • After a Hermes active_profile switch or a pull under OMP_PROFILE, uninstall cleans teamai's hook, plugin, config.yaml entries, approval and SOUL.md block (Hermes), or its extension (Oh My Pi), in every home and profile teamai wrote.
  • With HERMES_HOME recorded at init, uninstall from a shell without it cleans the Hermes hook and plugin, and so does a home re-recorded as <root>/profiles/work.
  • An Oh My Pi home moved with PI_CONFIG_DIR, then re-init under OMP_PROFILE, is cleaned in both profiles.
  • uninstall --agent trae keeps .trae/agents/reviewer.md while Trae CN still reads it. A later uninstall --agent trae-cn removes it.
  • Member-owned hooks, extensions, plugins and SOUL.md content in other profiles survive unchanged, and the plan lists every file before removal.
  • The Trae CN model pin survives push after an instructions-only edit and after an edit to another extra.

tsc and lint exit 0. The changed unit files pass (uninstall.test.ts 230/230, also with a symlinked TMPDIR). The PR's e2e file passes.

Non-blocking

  • [P3 nit] When uninstall --agent trae keeps shared agent files, the success line (src/uninstall.ts:2674) says only "its shared skills and rules stay". It could name agents too.
  • [P3 nit] The teamai skill copy in other Hermes and Oh My Pi profiles survives uninstall. The Hermes hook script is matched by file name rather than by its marker.

Not checked: real Hermes, Oh My Pi, OpenClaw and Trae binaries, and Windows beyond CI. The gitlab and github providers are left to CI.

@SaulMoro
SaulMoro merged commit 55838d1 into Tencent:main Oct 28, 2026
18 checks passed
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.

3 participants