Skip to content

fix(uninstall): remove what teamai created and left empty - #1817

Merged
SaulMoro merged 1 commit into
Tencent:mainfrom
Walternater:fix/uninstall-created-leftovers
Nov 10, 2026
Merged

SaulMoro merged 1 commit into
Tencent:mainfrom
Walternater:fix/uninstall-created-leftovers

Conversation

@Walternater

@Walternater Walternater commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

teamai uninstall now removes what it created and then emptied. The issue lists 15 entries the verify ledger knew about; this leaves 5, and the 10 it removes become a LEAK if they ever come back, because KNOWN_LEFTOVERS no longer tolerates them.

What decides an empty directory here is not that it is empty, but whether anything resolves a path through it. ~/.claude is a tool's own directory, ~/.teamai/projects is where every checkout's data home is looked up, and ~/.teamai holds the log — those stay. bin/, dashboard/ and locks/ only ever hold what teamai writes into them by name, so they go.

The decisions the issue asked for, entry by entry:

Left behind Decision Why
~/.bashrc, ~/.profile, ~/.zshenv (a 2 B newline) remove the file when the block was all it held A member's profile keeps its own lines; one that held only teamai's block is teamai's. A profile that existed before init and held nothing else goes too, which the CHANGELOG and docs/guide/faq.md now say.
~/.claude/settings.json (hooks cleared to empty lists) remove the file when it holds nothing else A settings file with member keys is rewritten, not deleted: only teamai's hooks leave.
~/.teamai/bin/teamai (and the Windows .cmd and CodeBuddy wrappers) remove Auto-generated, and marked Auto-generated by teamai.
~/.teamai/bin/, dashboard/, locks/ remove when empty Nothing looks these up; teamai writes into them by name.
~/.teamai/projects/ stays Every data-home lookup scans it, and a test that reads the records there failed with ENOENT when it was pruned.
~/.teamai/dashboard/session-owners.jsonl remove when empty teamai opens it; it holds nothing.
<project>/.claude/skills/ remove when empty teamai delivers into it.
~/.claude/, <project>/.claude/ stay A tool's own directory, not teamai's. I tried removing them first: with ~/.claude gone, Claude's MCP config stopped being written in mcp-uninstall — the command reported success and left team-mcp in ~/.claude.json. teamai removes what it put inside them, not the directories the tool reads.
~/.teamai/debug.log stays It is the record of an uninstall that failed, and that record is worth more than the file. Confirmed by the reviewer.
~/.teamai/ stays while the log and projects/ do

Small code, no new command: the emptied-hook-file deletion that Copilot already had now runs for every tool, a shell profile that ends up empty is removed instead of rewritten, and one sweep at the end of removal takes the wrapper, the empty dashboard file and the directories that held only them.

The sweep is skipped when this uninstall keeps shared resources (--agent), when a hook it could not remove is still there, and while any other install on the machine still runs HOME's hooks: installsSharingHome() covers the user scope and every partition, and keptForProjects() is read at sweep time rather than from the user-scope-only plan field.

Refs #1808. Fixes #1808 for the 10 entries above; the other 5 are the two tool directories, projects/, the log, and the root that holds them.

Validation

  • npm run lint — passes, including the documentation checks
  • npm run typecheck — passes
  • npx vitest run src/__tests__/uninstall.test.ts — 256/256 pass
  • Guard cases: a project-partition uninstall with a second checkout's data home keeps the wrapper (it fails against keptForProjects taken from the plan, which is filled for the user scope only); so does one while a user-scope install shares the HOME (it fails without installsSharingHome); and one with nothing else on the machine removes it. The first pair of cases here were vacuous before: with a user-scope config the data home is ~/.teamai itself, so the existing removeAllBut already took the wrapper.
  • Four existing suites changed with the behavior, all of them reading a settings file uninstall now removes: hooks-project-isolation-issue373 (absent file reads as no hooks), config-entry-ownership-993 (asserts the file is gone), submodule-checkout-1041 (absent reads as empty), copilot-global-hooks (same). codebuddy-mcp-local-915 found ~/.teamai/projects was pruned recursively by pruneEmptyDirs; the sweep no longer prunes the root, so it stays.

Real-CLI run, before and after

World from origin/main (09f3ca7d), world 20261110T095827-d7f1a6:

$ npm run -s verify:world && npm run -s verify -- teamai <run> init https://teamai.test/team/repo.git --provider git --agent claude
$ npm run -s verify -- teamai <run> pull && npm run -s verify -- ledger <run>
(e) After teamai uninstall
- home/.bashrc left behind, known: …/issues/1808
- home/.claude left behind, known: …
- home/.claude/settings.json left behind, known: …
- home/.profile left behind, known: …
- home/.teamai left behind, known: …
- home/.teamai/bin left behind, known: …
- home/.teamai/bin/teamai left behind, known: …
- home/.teamai/dashboard left behind, known: …
- home/.teamai/dashboard/session-owners.jsonl left behind, known: …
- home/.teamai/debug.log left behind, known: …
- home/.teamai/locks left behind, known: …
- home/.teamai/projects left behind, known: …
- home/.zshenv left behind, known: …
- project/.claude left behind, known: …
- project/.claude/skills left behind, known: …
Ledger: CLEAN

Same run on this head (ea437ba0, world 20261110T115430-01ee20), with KNOWN_LEFTOVERS cut to the five entries this change keeps:

(e) After teamai uninstall
- home/.claude left behind, known: …/issues/1808
- home/.teamai left behind, known: …
- home/.teamai/debug.log left behind, known: …
- home/.teamai/projects left behind, known: …
- project/.claude left behind, known: …
Ledger: CLEAN

Nothing else is left, and the 10 entries are no longer tolerated: if one comes back, the ledger reports a LEAK.

Change Surface

  • Tools: hook settings of every tool (the emptied-file deletion moved out of the Copilot-only branch); a shared settings file keeps the member's keys, and no tool's directory is removed.
  • Scopes: user and project/workspace; the project's .claude comes from the plan's projectRoot.
  • Team repo modes: not applicable — uninstall reads no team repo.
  • Checkouts: the project directories follow plan.projectRoot; a linked worktree's files are unchanged.
  • Layers: not applicable.
  • Entry points: teamai uninstall only. teamai hooks remove keeps its current behavior.
  • Reverse states: this is that rule, and the ledger is how it is seen.
  • OS: the wrapper names include teamai.cmd and the CodeBuddy wrapper; this was not run on Windows.
  • Docs and skill-data/: docs/guide/faq.md already says "Files you created stay", which still holds; CHANGELOG updated.

Limitations

  • uninstall --dry-run lists the resources it will remove, not these emptied files and directories. They hold nothing of the member's, so the preview stays accurate about what matters, but it is not exhaustive.
  • debug.log is left on purpose; see the table.
  • A member file that happened to hold nothing but teamai's block is removed with it. That is the rule the issue asks for; I found no counter-example in the code that writes those blocks.
  • The removal of an emptied settings file depends on gitUntracked: one a repository tracks stays, as the hook pass already keeps it.

@Walternater
Walternater force-pushed the fix/uninstall-created-leftovers branch from 1627d73 to 927cba3 Compare November 10, 2026 09:57
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Respect shared-resource retention — src/uninstall.ts:3320: The sweep ignores plan.includeShared. In a user installation with Claude and CodeBuddy and no registered projects, uninstall --agent claude must retain shared resources for CodeBuddy, but this sweep deletes both shared wrappers. CodeBuddy’s remaining hooks then reference missing executables. Skip global cleanup when shared resources are retained.

  • [P1 blocking] Keep wrappers when hook removal fails — src/uninstall.ts:3303: If settings permissions make reconcileHooks fail, the existing code records hooksLeft and preserves the data home for retry. The unconditional sweep nevertheless deletes its wrapper when no other projects are registered, leaving the retained hooks unable to execute. Preserve wrappers while unresolved hooks remain.

The PR description includes a representative before/after real-CLI world record, satisfying the testing requirement. No PR code was run, built, or installed.

@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 with the built CLI in verify worlds, on the merge base (531eb15f) and on 1627d739. 927cba33 is the same change rebased onto 09f3ca7d, so everything below applies to the current head.

The default path works: init, pull and uninstall end with verify reporting CLEAN and only the two known leftovers. debug.log staying, and KNOWN_LEFTOVERS cut to two, match the decisions in the body.

Blocking

  • [P1 blocking] The "another project still needs them" guard does nothing outside user scope. sweepCreatedLeftovers returns early when plan.keptForProjects.projects is non-empty (src/uninstall.ts:3320). That list is filled only when includeShared && globalAdapters (:1554), and globalAdapters is user scope only. In workspace scope, which is the default for init, the list is always empty. Setup:

    • two checkouts on one HOME, each with init --agent codebuddy in workspace scope;
    • teamai uninstall in checkout A.

    ~/.teamai/bin/teamai is removed. Checkout B's CodeBuddy hooks run PATH="$HOME/.teamai/bin:$PATH" teamai hook-dispatch … || true, so with no other teamai on the tool's PATH, B's hooks now find nothing and fail silently. On the merge base the wrapper stays. Computing the kept projects for every scope, or checking the other data homes before the sweep, would close it. A workspace-scope case with a second checkout in uninstall.test.ts would hold it.

Non-blocking

  • [P2 non-blocking] Uninstall deletes a member's empty files that existed before init.

    • Shell profiles. A pre-existing empty ~/.zshenv, or a ~/.bashrc with only whitespace, is removed once teamai's block leaves it (src/uninstall.ts:3136).
    • Settings files. A ~/.claude/settings.json that was {} before init is removed (:450-467, called at :2800), now for every tool. By reading the code, a member's {"hooks":{"Stop":[]}} qualifies too; I did not run that one.

    No content is lost, so this does not block. But the body says "a profile that held only teamai's block is teamai's", and that holds only when teamai created the file. Noting at write time whether the file existed, and removing it only when teamai made it, would make the rule exact. JSONC hook files survive today only because strict JSON.parse fails on them, which is luck rather than a rule.

  • [P2 non-blocking] The sweep runs after a failed removal. With a read-only settings file, uninstall ends with "Uninstall incomplete" and exit 1, yet the wrapper is removed. That settings file's hooks still call it, so the retry path loses the wrapper it needs. Skipping the sweep when any removal is left would fix it.

  • [P3 nit] teamai.cmd and teamai-codebuddy-hook are removed without the marker check that teamai gets.

  • [P3 nit] The CHANGELOG line could name ~/.claude and <project>/.claude, and say the rule covers files teamai created.

CI has not run at this head yet, and CHANGELOG.md was the only conflict with main before the rebase.

@Walternater
Walternater force-pushed the fix/uninstall-created-leftovers branch from 927cba3 to 6485eca Compare November 10, 2026 10:13
@github-actions

Copy link
Copy Markdown

Both earlier findings remain unresolved in the current diff:

  • [P1 blocking] Respect shared-resource retention — src/uninstall.ts:3320: The sweep checks registered projects but ignores plan.includeShared. With Claude and CodeBuddy installed at user scope and no registered projects, uninstall --agent claude deletes shared wrappers still needed by CodeBuddy, breaking its remaining hooks. Skip shared cleanup when the plan retains shared resources.

  • [P1 blocking] Keep wrappers when hook removal fails — src/uninstall.ts:3303: If reconcileHooks fails because settings are unwritable, the catch records hooksLeft, but the unconditional sweep still deletes the wrapper when no other projects are registered. The retained hooks then reference a missing executable. Preserve wrappers while unresolved hooks remain.

The PR description provides sufficient representative real-CLI before/after verification. Its older recorded head is not blocking. No PR code was run, built, or installed.

@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.

Checked 6485eca4. It adapts two real-CLI tests to settings files that uninstall now removes, which is consistent with the change, but it does not touch src/uninstall.ts. The points from my review stand. Two of them are blocking now, in line with the CI review at this head:

  • [P1 blocking] The sweep ignores plan.includeShared. sweepCreatedLeftovers (src/uninstall.ts:3303, :3320) checks only the kept-projects list. That list is empty outside user scope, and it is also empty when no other project is registered. So uninstall --agent claude, with CodeBuddy still set up, removes the wrapper CodeBuddy's hooks call. The same holds for the workspace-scope second checkout from my review. Skipping the sweep whenever includeShared is false, and computing the kept projects for every scope, covers both.
  • [P1 blocking] The wrapper goes after a failed removal. I had this at P2. The CI review rates it blocking, and our run reproduced it: with a read-only settings file, uninstall exits 1 with "Uninstall incomplete", yet ~/.teamai/bin/teamai is removed while the kept hooks still call it. The sweep should not run while hooksLeft, blocksLeft or filesLeft is non-empty.

The P2 on deleting a member's pre-existing empty files, and the two nits, are unchanged.

@Walternater
Walternater force-pushed the fix/uninstall-created-leftovers branch from 6485eca to 902215e Compare November 10, 2026 10:19
@Walternater

Copy link
Copy Markdown
Collaborator Author

All three blocking findings are fixed in 902215ef.

  • The scope guard did nothing outside user scope. You are right, and the fix is the one you suggested: the sweep now calls keptForProjects() itself at sweep time instead of reading plan.keptForProjects, which is filled only under includeShared && globalAdapters. keptForProjects() walks every partition on the machine, so a workspace-scope uninstall sees the second checkout. I added the case you asked for in uninstall.test.ts: a project-partition uninstall with another checkout's data home in ~/.teamai/projects/ keeps the wrapper; it fails against the version you reviewed (wrapper: expected false to be true) and passes now.
  • Shared resources retained by --agent. The sweep returns early unless plan.includeShared, so uninstall --agent claude with CodeBuddy still installed keeps the shared wrappers.
  • A hook that could not be removed. hooksLeft is now passed into the sweep, which skips while it is non-empty.

One thing worth flagging from writing that guard test: my two earlier cases were vacuous. With a user-scope config the data home is ~/.teamai itself, so the existing removeAllBut(plan.teamaiHome, kept) had already taken bin/teamai before my sweep ran — the assertions passed for a reason that had nothing to do with this change. Both cases now use a project partition (~/.teamai/projects/<name>), where the wrapper really does sit outside the data home, and the removal case still passes for the right reason.

World re-run after the guard change (20261110T101917-8b3b56, head 902215ef): the same two known leftovers, Ledger: CLEAN. npm run lint, npm run typecheck and uninstall.test.ts (255/255) pass.

The debug.log decision is unchanged and still the one item I would like your call on.

@SaulMoro

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Checked 902215ef. All three blocking items are fixed:

  • Shared resources. The sweep returns early unless plan.includeShared, so a partial --agent uninstall keeps the shared wrappers.
  • Failed hook removal. The sweep skips while hooksLeft is non-empty. filesLeft and blocksLeft hold skills, rules, docs and profile blocks, none of which calls the wrapper, so checking hooksLeft alone is enough.
  • Second checkout. keptForProjects() now runs at sweep time, in every scope.

Good catch on the two vacuous cases. Moving them to a project partition makes them prove what they claim.

On debug.log: keeping it is the right call. It is the only trace an uninstall that failed leaves, and verify lists it as a known leftover.

One point from my first review is still open before I approve:

  • [P2 non-blocking] A member's empty file that existed before init is still deleted. That covers an empty ~/.zshenv, a whitespace-only ~/.bashrc, and a ~/.claude/settings.json that was {}. Either record at write time whether teamai created the file, or say in the CHANGELOG that an emptied profile or settings file is removed even when it existed before. Which do you prefer?

The CI review at this head is still running.

@github-actions

Copy link
Copy Markdown

No new findings confidently supported by the scoped diff.

Both earlier findings are resolved: sweepCreatedLeftovers now skips cleanup when plan.includeShared is false or hooksLeft is nonempty.

The PR description includes sufficient representative before/after real-CLI world verification. The older recorded head is not blocking under the trusted review rules.

No PR code was run, built, or installed.

@Walternater
Walternater force-pushed the fix/uninstall-created-leftovers branch from 902215e to e3b0baa Compare November 10, 2026 10:52
@Walternater

Copy link
Copy Markdown
Collaborator Author

Round two, all self-inflicted and all found by running things rather than reading them.

Your two P1s, and the bot's two, are fixed in this push:

  • the sweep reads keptForProjects() at sweep time, so a workspace-scope uninstall sees the second checkout (the plan's copy is user-scope only), with the case you asked for in uninstall.test.ts;
  • !plan.includeShared skips it when --agent keeps shared resources, and hooksLeft skips it when a hook could not be removed.

Then CI and my own e2e run turned up a third problem, which is the interesting one.

I had the sweep prune ~/.claude and <project>/.claude when they were empty. The Windows job failed first on hooks-project-isolation-issue373 (it reads settings.local.json after uninstall), and E2E shard 1 failed on mcp-uninstall: with ~/.claude gone, the second uninstall reported MCP servers (1): claude/team-mcp, exited 0, and left team-mcp in ~/.claude.json. Dropping the two tool directories from the sweep makes that test pass unchanged.

So the decision changed: a tool's own directory stays. teamai removes what it put inside ~/.claude and <project>/.claude, not the directories the tool reads. KNOWN_LEFTOVERS therefore keeps four entries — those two directories, debug.log, and ~/.teamai that holds the log — and 11 of the 15 the issue lists are gone. uninstall still takes the emptied settings file, the emptied shell profile, the wrapper, the empty dashboard file, and the directories that held only teamai's things.

One process note that cost me an hour and is worth knowing for review: the e2e suites run dist, not src. My first bisect disabled the sweep in source, did not rebuild, and "proved" the sweep was innocent. After npm run build the same test passed with the sweep off. If a reviewer reruns a guard locally, rebuild first.

World re-run after the change (20261110T105022-de9241): four known leftovers, Ledger: CLEAN, no LEAK lines. npm run lint, npm run typecheck, uninstall.test.ts 255/255, and the eight e2e files touched or adjacent 147/147.

@SaulMoro

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Checked e3b0baa2. Leaving ~/.claude and <project>/.claude in place is the safer rule, and the mcp-uninstall failure shows why: the tool's own directory is not teamai's to remove. The KNOWN_LEFTOVERS entries and the submodule-checkout-1041 change match it. That test still fails if echo fixture survives in a file that is left.

The one open point from my previous comment still needs an answer before I approve: a member's empty profile or {} settings file that existed before init is deleted. Either record at write time whether teamai created the file, or say so in the CHANGELOG line. The line currently reads "removes what it created", which a member would read as excluding their own files.

CI and the CI review are still running at this head.

@github-actions

Copy link
Copy Markdown

No new findings confidently supported by the scoped diff.

Both earlier findings are resolved: sweepCreatedLeftovers skips cleanup when plan.includeShared is false or hooksLeft is nonempty.

The PR description includes sufficient representative before/after real-CLI world verification. An older recorded commit is not blocking under the trusted review rules.

No PR code was run, built, or installed.

@Walternater
Walternater force-pushed the fix/uninstall-created-leftovers branch from e3b0baa to 4575579 Compare November 10, 2026 11:20
@Walternater

Copy link
Copy Markdown
Collaborator Author

E2E shard 2 turned up the last one, and it changed the rule I was applying.

codebuddy-mcp-local-915 reads ~/.teamai/projects/ after uninstall to find each checkout's records. I had that directory pruned when empty — and it was empty — but pruneEmptyDirs recurses, so naming ~/.teamai was enough to take projects with it. It is back, and the sweep no longer prunes the root at all.

So the rule is not "an empty directory teamai made goes". It is whether anything resolves a path through it:

  • ~/.claude — the tool's own directory; removing it stopped Claude's MCP config write, as mcp-uninstall showed.
  • ~/.teamai/projects — every data-home lookup scans it; the records test failed with ENOENT without it.
  • ~/.teamai — holds the log, and now projects/.
  • ~/.teamai/bin, dashboard, locks — only ever hold what teamai writes into them by name, so an empty one goes.

Final count: 15 known leftovers to 5. Ten removed — the three shell profiles that held only the block, the emptied settings file, the wrapper, the empty dashboard file, bin/, dashboard/, locks/, and <project>/.claude/skills — and five kept with the reasons above. copilot-global-hooks was the fourth suite reading a settings file uninstall now removes; it reads an absent file as no hooks.

World 20261110T111858-b9dbf9 at e3b0baa2: five known leftovers, Ledger: CLEAN, zero LEAK lines. Eleven e2e suites touched or adjacent: 182/182. uninstall.test.ts 255/255.

debug.log is still the one item I would like your call on; projects/ and the two tool directories are now out of scope with the reason above, and I would rather leave them than break a lookup to make the ledger shorter.

@SaulMoro

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Checked 4575579f. "Keep a directory anything resolves a path through" is the right rule. Not pruning projects/ or the root follows from it, since pruneEmptyDirs recurses.

On debug.log: I answered this in my comment at 902215ef. Keep it.

That leaves the one point I have raised twice: a member's empty profile or {} settings file that existed before init is deleted, and the CHANGELOG says "removes what it created". Either of these is enough for me to approve:

  • record at write time whether teamai created the file;
  • one clause in the CHANGELOG line, such as "…including one that existed before init and held nothing else".

@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Preserve wrappers needed by the user scope — src/uninstall.ts:3326: With a user-scope Claude installation and one workspace, uninstalling the workspace sets includeShared to true and leaves the user’s HOME hooks intact. Once that workspace is removed, keptForProjects() returns no projects—it does not inspect the user installation—so the sweep deletes ~/.teamai/bin/teamai, breaking the retained user hooks. Check surviving user-scope consumers before removing machine-wide wrappers.

Both earlier findings are resolved by the includeShared and hooksLeft guards.

The PR description provides sufficient representative real-CLI verification; its older recorded commit is not blocking.

No PR code was run, built, or installed.

A shell profile whose only content was teamai's block became a file holding a
newline; a tool settings file whose only content was teamai's hooks stayed as
empty lists; the hook wrapper, an empty dashboard file and the directories that
held them stayed too. Uninstall now removes each of those, and the ledger's
known leftovers drop from 15 entries to 2 (the log and the directory holding
it).

Refs Tencent#1808
@Walternater
Walternater force-pushed the fix/uninstall-created-leftovers branch from 4575579 to ea437ba Compare November 10, 2026 11:56
@Walternater

Copy link
Copy Markdown
Collaborator Author

The P1 is fixed in the next push: the sweep now calls installsSharingHome(plan.teamaiHome), which returns the user-scope config and every other partition on the machine, and skips while any of them is left. keptForProjects() alone could not see the user scope, which is the gap you found. The case you describe is in uninstall.test.ts: a user-scope config in ~/.teamai/config.yaml, a project-partition uninstall, and the wrapper stays; it fails without the guard (wrapper: expected false to be true).

On the pre-existing empty file, I took your second option rather than recording creation at write time:

  • CHANGELOG.md now reads "…and the directories that held only those — including a profile or settings file that existed before init and held nothing else."
  • docs/guide/faq.md and docs/guide/zh-CN/faq.md said "Files you created stay", which was no longer the whole truth: both now say "…stay, unless taking teamai's part out of one leaves it empty."

I did not add write-time tracking because there is no per-file record to put it in — ~/.teamai has the delivery ledger for resources, not for shell profiles or tool settings — and the only case where the behavior differs is a file that holds nothing at all. If you would rather have the record, say so and I will add it; the clause is the honest description in the meantime.

debug.log: your 902215ef comment answered it — kept. The PR body no longer asks.

Latest world run and the suites are in the body; I will push once the local run finishes so the recorded commit matches the head.

@github-actions

Copy link
Copy Markdown

No new findings confidently supported by the scoped diff.

All earlier findings are resolved: sweepCreatedLeftovers preserves wrappers when shared resources are retained, hook removal fails, or another installation—including user scope—still shares HOME.

The PR description satisfies the testing requirement with representative before/after real-CLI world verification, including head ea437ba0.

No PR code was run, built, or installed.

@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.

Checked ea437ba0. Everything from my reviews is resolved:

  • Blocking items. The sweep skips while shared resources are kept (includeShared) and while a hook could not be removed (hooksLeft). It also skips while another install on this HOME is left: the user-scope config or another partition (installsSharingHome), and, as before, a registered project (keptForProjects). In the user-scope case the CI review raised, the user scope's hooks therefore keep their wrapper.
  • Pre-existing empty files. The CHANGELOG line and both FAQ pages now say that a file left empty by taking teamai's part out goes. That was the one point open. A write-time record is not needed for a file that held nothing.
  • What stays. The tool directories, ~/.teamai/projects, ~/.teamai and debug.log stay for the reasons in the body.

The CI review at this head has no findings.

Approving. I'll merge once CI is green at this head.

@SaulMoro
SaulMoro merged commit f67defd into Tencent:main Nov 10, 2026
19 checks passed
Walternater added a commit to Walternater/teamai-cli that referenced this pull request Nov 11, 2026
…them

A project uninstall kept Codex's ~/.codex/hooks.json, OpenCode's plugin and the
Pi, OMP, Kiro and Antigravity hook files for "other teamai installs (user scope,
HTTP agent or other projects)" without checking that one existed. On the last
install those dispatchers stayed, fired on every session and found no config,
with `teamai hooks remove` as the only way out.

The decision is the one Tencent#1817 added for the wrapper: installsSharingHome() for
the user scope and the other project partitions, plus the local-agent manifest
for an HTTP agent. When any of them is set up, nothing changes.

Refs Tencent#1825
Walternater added a commit to Walternater/teamai-cli that referenced this pull request Nov 11, 2026
…them

A project uninstall kept Codex's ~/.codex/hooks.json, OpenCode's plugin and the
Pi, OMP, Kiro and Antigravity hook files for "other teamai installs (user scope,
HTTP agent or other projects)" without checking that one existed. On the last
install those dispatchers stayed, fired on every session and found no config,
with `teamai hooks remove` as the only way out.

The decision is the one Tencent#1817 added for the wrapper: installsSharingHome() for
the user scope and the other project partitions, plus the local-agent manifest
for an HTTP agent. When any of them is set up, nothing changes.

Refs Tencent#1825
Walternater added a commit to Walternater/teamai-cli that referenced this pull request Nov 11, 2026
…them

A project uninstall kept Codex's ~/.codex/hooks.json, OpenCode's plugin and the
Pi, OMP, Kiro and Antigravity hook files for "other teamai installs (user scope,
HTTP agent or other projects)" without checking that one existed. On the last
install those dispatchers stayed, fired on every session and found no config,
with `teamai hooks remove` as the only way out.

The decision is the one Tencent#1817 added for the wrapper: installsSharingHome() for
the user scope and the other project partitions, plus the local-agent manifest
for an HTTP agent. When any of them is set up, nothing changes.

Refs Tencent#1825
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.

teamai uninstall leaves files and directories that init and pull created

3 participants