Saul Moro 5fbaa377c7 feat(recall): attribute recall and adoption to the agent session in every agent (#925)
* refactor(usage): extract the locked JSONL store from the usage file (#884, ticket 01)

Move the usage file's lock, side-record, fold and rewrite protocol into
src/utils/jsonl-store.ts so the recall log can reuse it. The usage file
keeps its behavior: same waits, mode, side-file names and gitignore healing.

The store adds what the recall log needs: reads that include unfolded side
records, owner-only files by default, and a newline before an append when a
killed writer left a torn last line.

* feat(recall): attribute a Claude direct recall to the doc it opens (#884, ticket 02)

recall prints run=<id> on the region start line and appends a run record
(session from the environment, each doc's vote key, scope, printed path and
eligibility; never the query) to the active scope's recall log. A new
PostToolUse handler appends a claim for a Bash call that ran teamai recall,
and evidence for a Read under the knowledge roots. votes-sync now credits
through the recall-log reducer instead of the transcript parser and no longer
needs transcript_path.

* feat(recall): exclude the recall subagent's own reads, mark its runs with --caller (#884, ticket 03)

Claims and evidence carry the actor (session, agent_id, agent_type). A run
is marked as the teamai-recall subagent's by its hidden --caller or its
claim's agent_type; reads by that actor never count for it, while reads by
the main agent or any other subagent do. The recall agent passes
--caller teamai-recall on every recall and drops the unread
referenced-doc-ids instruction.

* feat(recall): settle each run by a valid claim or an unambiguous env session (#884, ticket 04)

A run now records its agent family, via (env or none) and whether the
environment held a single session id. The hook records a claim for every
run id a shell call printed, noting whether its command ran teamai recall
itself (parsed, so a quoted mention does not count); it also reads Codex's
string tool_response. The reducer applies only the earliest direct claim for
a run in the log, else the env session when unambiguous; unsettled runs
never vote. agentSessionIdFromEnv callers are unchanged.

* feat(recall): count a shell reader's read of a recalled doc, the Codex path (#884, ticket 05)

Add the tool-call classifier (src/utils/tool-call.ts): category, paths read
and status per PostToolUse, replacing recordToolCall's Bash and Read branches.
The shell tokenizer moves to src/utils/shell-command.ts and now returns each
simple command's operator and redirects; invokesRecall and the reader check
share it. A read is one reader command (cat, bat, batcat, less, more, head,
tail, nl, sed -n printing lines) alone or at the head of a pipeline; any ;,
&&, || or & makes the call no read. A failed read is not recorded; a read of
unknown status (Codex's string tool_response) counts only when simple.

* feat(recall): count a search that shows a recalled doc's lines, never a listing or a count (#884, ticket 06)

A search tool in content mode, or a shell grep/rg/ag/ack/git grep, records
evidence for each file under the knowledge roots whose lines its output
shows: a line that starts with the file's path and ':', under a searched
root, or the lone operand when the output is non-empty. The hook only scans
the output it already holds; it logs resolved paths, never the command or
the output. Listings (Glob, ls, find, fd, tree, rg --files, git ls-files,
grep -l/-L, a Grep file list) and counts (-c, --count, count mode) record
nothing. OMP's and Cursor's search tools produce no evidence yet.

* feat(recall): count Windows reads: PowerShell readers, drive paths, Git Bash /c/ (#884, ticket 07)

PowerShell (Claude, CodeBuddy, Copilot) is a shell tool. Get-Content, gc,
type and cat read their -Path, -LiteralPath and positional files. Paths are
resolved and compared by the new agent-path module, independent of the host:
drive-letter case, either separator and Git Bash /c/ name one file, and a
search line's drive colon is part of its path.

* fix(recall): compare Windows paths without case, look up shell verbs by own key (#884, ticket 07 follow-up)

Windows paths ignore case, so a drive-lettered path's comparison key is now
lowercased whole, as isUnderRoots did through path.win32.relative before
ticket 07. A verb such as `constructor` no longer resolves to an
Object.prototype member in the reader and search tables.

* feat(recall): count Cursor, Copilot, CodeBuddy, WorkBuddy, Qoder and ZCode reads (#884, ticket 08)

Session derivation reads Cursor's conversation_id after session_id and
sessionId. The classifier reads Cursor's tool_output JSON string and
Copilot's tool_result (text_result_for_llm, result_type), a read's path from
file_path, filePath or path, and the names view, ReadFile, bash, Shell and
run_in_terminal. Unverified payload shapes are marked in the row names.

* feat(recall): OpenCode bridge sends its session, output and status, and links task children to their parent (#884, ticket 09)

* feat(recall): Pi and OMP bridges send their session, output and status; OMP subagents their agent id and type (#884, ticket 10)

* feat(recall): credit late reads at SubagentStop and pull, and prune the recall log at pull (#884, ticket 11)

- Register SubagentStop for Claude Code, Codex, CodeBuddy and Qoder (and
  their internal builds); votes-sync runs the reducer there, without the
  end-of-turn summary.
- `teamai pull` credits sessions whose evidence is still pending, then
  prunes the log under its lock: 30 days, then 5000 lines, never dropping
  pending evidence younger than 24 h or the run, claim, credited reads and
  links it needs to vote. Evidence and its consumed lines go together.
- A run's doc is credited once, so a late read drained after the ledger's
  24 h window cannot vote twice.
- The store gains appendJsonlBatch (one lock, one write, one side record);
  a hook call and a reducer pass each make at most one write. Folds now
  work per line.

* refactor(recall): the upvote judge skips docs in the session's ledger, and the transcript parser loses its adoption logic (#884, ticket 12)

The opt-in judge (TEAMAI_UPVOTE_JUDGE) excluded the parser's adoptedDocIds on top of the session's upvote ledger. Since votes-sync credits from the recall log, a doc the parser counted but the hook path did not (a Glob listing, say) was neither credited nor judged. The judge now excludes only the ledger, and the parser keeps its recalled-doc detection, region parsing and recalled-doc-ids comment for the judge and older-format sessions.

* fix(recall): the upvote judge credits the turn before reading the ledger (#884, ticket 12 follow-up)

The dispatcher starts the detached judge before the foreground votes-sync
credits the turn, so a doc opened in that turn was sent to the judge CLI
once. The judge now runs the same reducer pass first; the ledger and the
consumed marks make a second pass a no-op.

* feat(stats): a recall section lists the last 10 sessions' runs, recalled and adopted docs (#884, ticket 13)

* fix(stats): name the agent whose hook claimed a recall run (#884, ticket 13 follow-up)

A claim now records the dispatch tool id of the hook that sent it, and the
recall section prefers it over the run's environment family. A Codex run
started from a Claude shell, or an OMP run, showed '-' before.

* docs(recall): one adoption section with the per-agent table and known limits (#884, ticket 14)

The usage guides gather adoption, the recall log, run ids, run ownership,
subagents, vote timing and the stats section under "Recall adoption and
upvotes", with a direct-recall / subagent-path table per agent and the
known limits. The core skill's troubleshooting reference says where a
recalled doc gets its upvote, and the recall subagent no longer says the
Stop hook parses its doc-id comment for adoption.

* docs(skill): CodeBuddy and WorkBuddy hooks are installed, not skipped (#884)

The core skill's hooks table said CodeBuddy and WorkBuddy hooks are skipped
by design, which contradicts the settings.json injection in src/hooks.ts and
the per-agent adoption list this PR adds to the same file.

* test(hooks): Claude settings carry the SubagentStop hook (#884, ticket 11 follow-up)

Ticket 11 registers votes-sync on SubagentStop for Claude; the hooks e2e
still expected four events. Cursor keeps four.

* fix(recall): a search line counts only from a file-name prefix, and the log keeps only .md paths (#884, review finding 1)

A one-file search's lines carry no path, so text before a colon (Cause:,
a -n false Grep line, Pi's basename) no longer becomes <doc>/<text> in the
recall log. Other lines need a prefix ending in a file name, then :<line>:,
or : in grep/rg path:text and OpenCode's header. The recorder drops any
evidence path that is not .md.

* fix(recall): a one-file search counts only when it shows a line, not a tool's no-match text (#884, review finding 2)

OpenCode's No files found and Pi's and CodeBuddy's No matches found made a
grep of the doc that found nothing credit it. The target now needs a line
that is neither blank nor a search tool's status line. Shell searches
print none, so their lines are never taken for one.

* fix(recall): the upvote judge keys each doc by the key its run recorded, not the parser's basename (#884, review finding 3)

The transcript parser names learnings/setup and a skill's SKILL.md by
their basename, so a doc the hook path had credited was judged again and
upvoted under another doc's key. recalledKeyOf maps the printed path to
the run's key for the ledger check and the upvote; a path no run printed
keeps the parser's id.

* fix(recall): SubagentStop credits locally and leaves the vote push to the next Stop (#884, review finding 4)

The main agent waits on SubagentStop, so the git round trip of the
reports push (and the ledger prune) blocked it mid-turn, up to the
handler budget, after every subagent while deltas were pending. Stop
and pull push what it credited. Stop is unchanged.

* docs(hooks): state only what is known about Codex and an unknown hook event key (#884, review finding 6)

Codex 0.159 knows SubagentStop, and Codex main's HookEventsToml has no
deny_unknown_fields. How older hook-capable builds parse an unknown key is
unverified.

* docs(skill): restore the Claude hooks row to its main wording (#884, review finding 7)

The CodeBuddy/WorkBuddy row keeps its fix, which the adoption section
below needs; the Claude row's rewording was unrelated to #884.

* refactor(recall): the adoption window and retention constants are module-private (#884, review finding 8)

Nothing imports them, tests included.

* fix(recall): an unquoted # that starts a word begins a shell comment (#884, PR review finding 1)

`cat other.md # /kb/doc.md` reads only other.md, yet the doc counted as
an operand. The comment runs to the end of the line; `a#b` and a quoted
`#` stay literal. A claim with a trailing comment still claims.

* fix(recall): an unquoted backslash escapes a space or shell character, and keeps a Windows path whole (#884, PR review finding 2)

`cat /kb/my\ doc.md` was split into two operands. Outside quotes a
backslash now escapes whitespace and ' " \ $ ` ( ) & ; | < > * ? # !;
before any other character it stays literal, so an unquoted
`C:\kb\learnings\x.md` is unchanged. Double quotes already followed
POSIX for \" and \\, and single quotes stay literal.

* fix(recall): a claim skips the root program's options before recall (#884, PR review finding 3)

`teamai -v recall "q"` is a valid recall, but its claim was marked
indirect, so a run with no session variable (OMP, via: none) never
settled. The root options now live in one table that index.ts registers
on the program and the claim parser reads through Commander's Option, so
it also skips the value of an option that takes one. `npx teamai-cli[@v]`
is handled as before.

* fix(recall): with no status, a shell call that printed only its command's errors failed (#884, PR review finding 4)

Codex sends no status, so `grep needle /kb/doc.md` answering
"grep: /kb/doc.md: Permission denied" counted as the doc's content and
upvoted it; `cat doc` answering "cat: doc: No such file or directory"
did the same. When the status is unknown, output lines that start with
the command word (as written or by name) and ": " are errors, not
content. A read or search that printed nothing else is a failure and
records no evidence. Both usage guides say so.

* fix(recall): Copilot's SessionEnd credits and pushes the session's votes (#884, PR review round 2 item 1)

Copilot's last turn can end with SessionEnd and no Stop, so votes-sync now
runs on session-end too: foreground, git only, with Stop's timeout. It
pushes as Stop does and prints no adopted summary. The Copilot rows end with
the real SessionEnd payload instead of a synthetic stop.

* fix(recall): a search with no hits prints its run id, so its shell call can claim it (#884, PR review round 2 item 2)

The no-hit line now ends in run=<id>, and the claim pattern reads it after
the logger's glyph. An OMP recall with no hits and no session variable
settles through its bash call's claim and shows in teamai stats. --check
still prints no run id and records no run.

* fix(recall): type and gc read only in PowerShell or for a Windows path, and a shell's own diagnostic is an error line (#884, PR review round 2 item 3)

In bash, type is a builtin that prints what a name is, so Codex's
type /posix/doc.md under Linux gave a false upvote with no status. type
and gc now count under a PowerShell tool, or when every file is a Windows
path. Get-Content counts everywhere. With no status, bash:, sh: and zsh:
diagnostics are error lines too.

* fix(recall): with no status, a read drops a file its command's error names (#884, PR review round 2 item 4)

In cat a.md doc.md where only doc.md errors, doc.md still counted. An error
line <verb>: <file>: … now removes that file from the read's paths, matched
as written and as resolved against the cwd; the other files still count.
2026-09-30 22:08:44 +08:00
…
…
…

teamai-cli

TeamAI — Make Every Team AI Native

Tencent%2Fteamai-cli | Trendshift

English | 中文 | 日本語 | 한국어 | ไทย

CI npm version npm downloads License: MIT

The shared foundation for how your team works, learns, and improves with AI.

TeamAI turns individual AI capabilities into shared team capabilities — across agents, machines, and team members.

Why TeamAI

Eight everyday scenarios, before and after TeamAI

Quick Start

Send this one line to your AI tool:

Install the teamai skill: https://github.com/Tencent/teamai-cli/tree/main/skills/teamai , load the teamai skill, then set up TeamAI for my team from scratch.

Once TeamAI is set up, just talk to the /teamai skill in your AI tool:

Set up a team from scratch

/teamai Help me set up TeamAI for my team from scratch

Join a team

/teamai Help me join my team's TeamAI, repo URL is https://github.com/your-org/your-repo

Share with the team

Skills, rules, MCP servers, and other agent resources can all be shared:

/teamai Share my xxx skill with the team

Open the dashboard

/teamai Open the TeamAI dashboard

Once a teammate is set up, they just open their agent and already have the team's full set of AI assets.

Command-line install

Install

npm install -g teamai-cli

Team admin / solo user

Create a shared-experience repo on your git host (GitHub, GitLab, GitCode, CNB, TGit, or a private Git service), grant write access to team members, then run teamai init https://github.com/your-org/your-repo.

No team repo yet? Start from a template pre-loaded with production-ready skills, rules, and review agents. Browse the teamai-hub org, click Fork, then teamai init against your new repo.

Team members

# Choose one, depending on where you want resources installed

# Project-scope init (default, resources installed under the project directory)
cd /path/to/my-project
teamai init https://github.com/your-org/your-repo

# Or, user-scope init (resources installed under ~/)
teamai init https://github.com/your-org/your-repo --scope user

Once initialized, every AI session automatically pulls the latest skills / rules and other Harness updates published by admins — no manual sync needed.

Product Overview

Three layers of capability, built on Git:

  • Team Execution — make every agent work the team's way: skills, rules, docs, env, agents, hooks, MCP, models.
  • Team Context (beta) — make every agent understand the team: learnings, codebase graph, teamwiki.
  • Team Improvement (beta) — make every execution improve the team: usage, sessions, dashboard.
Agent Team Execution Team Context (beta) Team Improvement (beta)
skillsrulesdocsenvagentshooksmcpmodels learningscodebaseteamwiki usagesessionsdashboard
Claude Code✓✓✓✓✓✓✓✓✓✓✓✓✓✓
Codex✓✓✓✓✓✓✓✓✓✓✓✓✓✓
Cursor✓✓✓✓✓✓✓—✓✓✓✓✓✓
GitHub Copilot CLI✓✓✓✓✓✓✓—✓✓✓✓✓✓
CodeBuddy✓✓✓✓✓✓✓✓✓✓✓✓✓✓
WorkBuddy✓✓✓✓—✓✓✓✓✓✓✓✓✓
OpenCode✓✓✓✓✓✓✓✓✓✓✓———
Pi Coding Agent✓✓✓——✓—✓——————
OpenClaw✓✓✓✓————✓✓✓———
Hermes✓—✓✓————✓✓✓———
DeepSeek Harness✓—✓—————✓✓✓———
Qoder✓✓✓✓✓✓✓—✓✓✓✓✓✓
Qoder CN✓✓✓✓✓✓✓—✓✓✓✓✓✓
Kiro✓✓✓✓✓✓✓—✓✓✓✓✓✓
ZCode✓—✓—✓✓✓—✓✓✓✓✓✓
Oh My Pi✓✓✓✓✓✓✓—✓✓✓———

Learn More

Contributors

Thanks to everyone who has contributed to TeamAI!

Contributors

Made with contrib.rocks.

Contributing

Join the conversation, or open an issue or PR. See CONTRIBUTING.md for how to contribute.

License

MIT

S
Description
GitHub Trending: Tencent/teamai-cli
Readme MIT
25 MiB
Languages
TypeScript 99.7%
Python 0.2%
JavaScript 0.1%