mirror of
https://github.com/Tencent/teamai-cli.git
synced 2026-10-02 03:14:40 +08:00
main
125
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f19f9de23a |
ci(lint): enable type-aware linting with no-floating-promises and no-misused-promises (#836) (#863)
* ci(lint): enable type-aware linting with no-floating-promises and no-misused-promises tsc does not report a promise nobody awaits or an async callback handed to an API that ignores its result. In a CLI that is an error nobody sees. oxlint's --type-aware mode checks both through oxlint-tsgolint. - package.json: add oxlint-tsgolint 7.0.2003 (oxlint 1.85.0 needs >=7.0.2001) and pass --type-aware to `npm run lint`. The package ships prebuilt binaries as optional dependencies with no install script, so CI's `npm ci --ignore-scripts` needs no change. - .oxlintrc.json: turn on typescript/no-floating-promises and typescript/no-misused-promises. Under src/__tests__/**, no-misused-promises skips checksVoidReturn.arguments: vi.spyOn(fse, ...) types the spy from fs-extra's last overload, the callback one that returns void, so every correct async mockImplementation is flagged. - --type-aware also turns on oxlint's default type-aware rules. The ones with hits on main stay off, since each is its own evaluation under #836. The rest of the defaults have no hits and stay on. Fixes, all behavior-neutral: - dashboard.ts: the SSE debounce and the PID check become named async functions called with `void`; both already catch everything they throw. The HTTP handler becomes handleRequest, and createServer catches its rejection, logs it and answers 500. - import-repo-list.ts: track in-flight imports in a Set, so the unused array splice returned is gone. - scripts/mock-teamai-server.mjs: same handler wrapper as the dashboard, so a malformed JSON body answers 500 instead of an unhandled rejection. - ai-client.test.ts: schedule the mock process events with queueMicrotask. Refs #836. * ci(lint): turn on typescript/no-useless-default-assignment 1 prod hit, type-only: syncTeamUpdatesToLocal's last parameter was `placedRules: Record<string, string> | undefined = undefined`; it is now `placedRules?: Record<string, string>`. Refs #836. * ci(lint): list only the type-aware rules that would otherwise run as off consistent-return, no-unnecessary-boolean-literal-compare, no-unnecessary-type-arguments, no-unnecessary-type-assertion, no-unnecessary-type-conversion, no-unnecessary-type-parameters and no-unsafe-type-assertion are not default rules, so listing them as off changed nothing. The config keeps off only the nine default type-aware rules that --type-aware would turn on. `oxlint --type-aware --print-config` gives the same set of active rules before and after. Refs #836. |
||
|
|
f7da1bb8b6 |
ci(lint): add oxlint and fail CI on any warning (#828) (#839)
* fix(tags,roles): honor --dry-run in tags subscribe, tags unsubscribe and roles set (#836) The three commands saved the local config and reset lastPullRev even under the global --dry-run, which is documented as "Preview mode, no changes made". Their siblings (tags add/remove, roles init/add/remove/ update) already return early with a [dry-run] message. The roles set preview names the additional roles it would save, including none, because a real run replaces the existing list. oxlint reported the unused options parameter in tagsSubscribe and tagsUnsubscribe; rolesSet has the same bug but reads options.add. * chore(lint): add oxlint with its default rules Pinned to an exact version so a new default rule arrives in its own PR, not as a CI failure on an unrelated one. The no-unused-vars options keep oxlint's _ ignore patterns and add ignoreRestSiblings, which the rest-omit in dashboard.ts relies on to keep config and roots out of /api/workspaces. * style(lint): apply oxlint safe fixes Drop redundant escapes in regex character classes and template literals, empty-object fallbacks in object spreads (spreading undefined adds nothing), and anchored regexes that are plain startsWith/endsWith checks. No behavior change. * refactor(lint): remove unused imports and an unused catch binding Applied with oxlint --fix-suggestions and reviewed by hand. Every removed whole import is a library module with no import-time side effects. * refactor(lint): remove dead code reported by no-unused-vars Each hit was checked against its callers and git history; none is missing wiring (the two that were, tags subscribe/unsubscribe, are fixed in the preceding commit). Removed: unused locals and functions, the options parameter of tagsList, rolesList and generateDigest (read-only commands), the never-read interactive option of importFromRepo, the empty test/e2e.mjs left over from the E2E migration, and a try/catch that only rethrew. new Array(n) becomes Array.from. No behavior change. * test(lint): fix lint hits in tests - contribute dry-run test asserted nothing; it now checks that the run leaves the repo/HOME tree unchanged (verified to fail when the dry-run early return is removed). - Drop a no-op expect(result).not.toThrow on a string. - Keep undefined in two optional-chain casts so a regression fails the assertion instead of throwing a TypeError. - Remove unused locals, helpers and imports; new Array(n) becomes Array.from. * refactor(lint): write control-character classes as \p{Cc} no-control-regex flags literal control ranges. \p{Cc} names the same set (C0, DEL, C1) and reads as what it means. Checked against the old classes on every code point from U+0000 to U+10FFFF: manifest-schema and agent-format match exactly, and contribute-check's normalization pipeline produces the same output. The test assertion is now stricter and checks every control character the sanitizer removes. * refactor(lint): remove disable directives for rules that are not enabled Four eslint-disable comments named rules this repo never ran (no-await-in-loop, @typescript-eslint/no-explicit-any), so they suppressed nothing. * ci(lint): fail CI on any oxlint warning (#828) npm run lint runs oxlint --deny-warnings and runs before the type check in both GitHub Actions and Coding CI. The repo is at zero warnings, so new code must stay clean. --report-unused-disable-directives also fails on a disable comment that suppresses nothing, so a suppression cannot outlive the code it was written for. CLAUDE.md, AGENTS.md, CONTRIBUTING.md and the PR template list the command so contributors and agents run it before opening a PR. Closes #828 * chore(lint): pin oxlint 1.16.0, the newest release that accepts Node 20.0 oxlint 1.17.0 and later declare engines.node ^20.19.0 || >=22.12.0, while the repo supports Node >=20. 1.16.0 declares >=8, supports --deny-warnings and --report-unused-disable-directives, and reports 0 warnings on this branch. * Revert "fix(tags,roles): honor --dry-run in tags subscribe, tags unsubscribe and roles set (#836)" This reverts commit |
||
|
|
21cb76aa49 |
feat: one namespace model for every resource type (#707) (#816)
* chore: start one namespace model for every resource type (#707)
* refactor(pull): check agent and skill namespace collisions with one resolver (#707)
Add src/namespace-resolver.ts, the pure rule tickets 02-05 build on: an
active namespace item replaces the root item of the same name, and a name
twice in one place or in two active namespaces is a tagged conflict naming
both sources. The result depends only on the active order, not read order.
Agents and skills now run their duplicate checks through it and throw the
same messages. Agents still treat root + namespace as an error.
Add fast-check for the resolver's property tests.
* feat(env,hooks,mcp): scope env, hooks and MCP servers by namespace (#707)
env/<ns>/env.yaml, hooks/<ns>/hooks.yaml and mcp/<ns>/mcp.yaml are read where
<ns> is active in resources.env/hooks/mcp; a namespace entry replaces the root
entry of the same key, hook id or server name. A broken active file, a name
twice in one file or in two active namespaces stops that type for the run and
keeps what is installed. Per-entry projects: (and roles: on env) reach nobody;
roles: on hooks and MCP keeps filtering with a deprecation warning. Unknown
resources: keys warn instead of failing the manifest.
* feat(pull): let an active namespace item replace the root item for skills, agents, rules and claudemd (#707)
With a role or project configured, an item in an active namespace now
replaces the root item of the same name, whole:
- agents by stem: root + namespace is no longer a duplicate error, and a
recorded (placed) agent replaces the root agent too
- skills by name, including a root skill received through a tag; an
install removes the files of the version it replaces
- rules by first-level file name, in tool dirs and Hermes' SOUL.md block
- claudemd files by name in the managed block
Two active namespaces with one rule or claudemd name stop that type for
the run and keep what is installed. Push writes an edited overridden
skill, agent or rule back to its namespace, and the skills push scan
covers role and project namespaces. The placement record is withdrawn
by a same-name shared-root file only in legacy mode. Recall indexes the
skills pull delivers. doctor lists overrides, and in legacy mode repeated
names, as notes. Legacy mode is otherwise unchanged.
* fix(pull): deliver both namespace rules and claudemd files of one name (#707)
Rules and claudemd have no namespace-vs-namespace conflict: each
namespace rule keeps its own local path and each claudemd file its own
place in the block, so two active namespaces with one first-level name
are both delivered, as before. Only root suppression applies.
doctor override and legacy repeated-name notes now use the same wording
as the env, hooks and MCP ones. The usage guide and admin reference say
to keep overridable shared content at the root, with an example.
* fix(pull): stop only skills or agents on a namespace collision (#707)
Two active namespaces with one skill name or agent stem used to throw
and abort the whole scope, so rules, env, docs, cleanup and the search
index were skipped too. resolveDesiredSkills, resolveDesiredAgents,
scanRoleAwareSkills and filterAgentsByNamespaces now return a tagged
conflict. pull warns, leaves that type as installed (no install, no
inactive-namespace sweep) and syncs the rest. doctor reports the
collision as before; recall indexes no skills while it stands.
* feat(env,hooks,mcp): namespace flags, origins in status and doctor, docs (#707)
env add/remove take --role/--project; remove mcp searches every file and asks
for --role/--project when several define the name; push picks up
env/<ns>/env.yaml. status, list and doctor show where each entry comes from;
doctor lists overrides as notes and per-entry roles:/projects: as one
informational check. Usage guides and the admin skill reference describe the
namespace files; the #668 e2e moves onto them.
* test(env): show a broken env file stops env only (#707)
* fix(remove): remove an MCP server from the root file by default (#707)
remove mcp <name> follows push's convention: mcp/mcp.yaml when it defines the
name, else the one namespace file that does; --role/--project pick a namespace.
Only a name several namespace files (and not the root) define is refused.
* test(remove): expect namespace files in sorted order (#707)
* feat(docs): deliver a declared docs namespace only where it is active (#707)
A docs/<ns>/ that any role or project lists under resources.docs now
reaches only members with that namespace active; an undeclared
docs/<dir>/ stays shared. Leaving a namespace removes its local docs that
are byte-equal to the team copy and keeps edited ones with a line.
team-codebase is rejected as a docs namespace. The search index (pull,
recall, contribute) and doctor's "Team docs delivered" use the same set.
* feat(models): scope team model profiles by namespace and bind keys to their gateway (#707)
models/<ns>/models.yaml, declared under resources.models, replaces the root
profile with the same id while <ns> is active. A team API key is stored per
profile id and base_url origin, so pull never writes a key next to a gateway
on another origin; it prints the models switch line instead. Conflicts and
broken files stop model updates for the run. models list and doctor show
where each profile comes from; push validates every models file.
* docs(models): describe model profiles by namespace and key binding (#707)
* docs: list docs among the axes declared by hand (#707)
* docs: describe one namespace model for every resource type (#707)
Rewrite the multi-project design doc's precedence section for
namespace-over-root, add the per-type conflict, failure and legacy-mode
rules, and replace the per-entry key rows in the product overview. The
JSON doctor notes now also carry namespace notes.
* docs(changelog): replace per-entry scoping with namespace files (#707)
Drop the beta-only per-entry projects: entry, add the namespace axes,
the override, the per-type failure policy, the roles: deprecation on
hooks and MCP, and the upgrade-every-member-first note.
* docs(changelog): say the model key binding re-keys once and affects only betas (#707)
* refactor(namespaces): one warn-once registry instead of the quiet flag (#707)
Namespace fallback warnings, entry notices and unknown resources: keys
now go through utils/warn-once, reset once per pull, so the quiet option
threaded through eleven signatures is gone. The one-line wrappers
resolveTeamEnv, resolveTeamMcpServers and resolveTeamProfiles are removed;
every caller uses resolveEntries/resolveEntriesFor with the type's reader.
* refactor(namespaces): shared entry-file helpers, no unsafe casts in new code (#707)
- listEntryFiles/entryFileAbsolutePath replace the per-type file listers
in env, mcp and models and the repeated path joins.
- gatewaySuffix replaces three spellings of the gateway suffix.
- LATER_RESOURCE_TYPES and friends are named for what they are:
HAND_DECLARED_RESOURCE_TYPES, HandDeclaredNamespacesShape.
- Error messages use instanceof Error; manifest role/project ids are
narrowed instead of cast; mapResources builds a typed object; pull
writes env through an EnvHandler instance instead of a cast.
- status keys counts by entry type; rules localNameFor reuses
deliversEveryNamespace, whose false answer is now documented.
* refactor(desired): move the desired-set resolvers out of pull.ts (#707)
Commands must stay thin, and recall, contribute and doctor imported
pull.js only to learn what a member receives. The skills, agents, rules
and claudemd resolvers, RolePullContext and the index sources now live in
src/resources/desired.ts; root suppression, the override note and the
repeated-name grouping live in namespace-resolver.
- A skill or agent conflict is a tagged DeliveryConflict carrying the
resolver's NamespaceConflict, rendered once by describeDeliveryConflict
(wording unchanged); DesiredItems names the result union.
- DesiredItems keeps each override, so doctor no longer rebuilds skill
and agent overrides by hand.
- recall and contribute share deliveredIndexSources; pull indexes through
the same indexedSkills instead of a second copy.
- doctor: one unresolvableCheck for skills, agents and docs; the docs
check reports an unreadable manifest instead of returning nothing; the
namespace notes catch only the team-repo reads.
- docs withdrawal reuses utils pruneEmptyDirs.
* fix(pull): name both files in a skill or agent conflict (#707)
Story 8 asks for a message naming both files. A skill or agent conflict
named only the namespaces, and an agent defined twice inside one
namespace (agents/a/x.md next to agents/a/x.yaml) read as 'found in
active namespaces "a" and "a"'. The duplicate case now names its one
place, and both cases list the two files.
* fix(rules): only a delivered namespace rule replaces the root rule, in every tool (#707)
- The rules override ran before the tag filter, so a namespace rule the
member's tag subscription excludes still suppressed the root rule and
the member received neither. The tag filter now runs first.
- JoyCode, OMP, Pi and Copilot share their rule directory with the
member's own rules, so the stale sweep deletes nothing there unless a
tombstone names it: the root rule a namespace rule replaced stayed
installed and both versions loaded (story 6). pullAllRules now removes
such a copy while it is byte-equal to its render, as agents do.
* fix(recall): keep the indexed skills while a skills conflict holds them (#707)
On a skills conflict pull keeps the installed skills, but the index was
rebuilt with none, so recall returned none of the skills the member still
has. The index now keeps the skills entries it already held, and pull
does the same when resolving the skills fails.
* fix(hooks,mcp): fail hooks inject and mcp inject when team entries do not resolve (#707)
reconcileTeamHooksForConfig returned [] when the team hooks could not be
resolved, the same value as a team without hooks, so hooks inject printed
'Hooks injected into all AI tool settings' and exited 0 over a broken
hooks/<ns>/hooks.yaml. It now returns { ok: false }, and hooks inject
exits 1 after the warning that names the file. mcp inject said 'Already
up to date.' in the same case; the MCP reconcile now marks the result
unresolved and mcp inject exits 1.
* fix(models): bind a beta API key to the gateway it was sent to, once (#707)
- A key a 0.26.0 beta stored under team:<id> counted for whatever origin
the root profile had now, so a root profile moved to another host got
the old key written next to it. The first pull or models command that
reads such a key now binds it to the origin TeamAI last wrote into the
agents switched to that profile (the root's current origin when it is
among them), else to the root's current origin, and never re-reads the
unbound key. Pull then leaves a moved agent alone and asks for the
switch.
- The 'switch to set a key' and 'no longer active' lines are written to
debug.log too: SessionStart pulls run silent.
- Legacy mode, which reads no namespace, says a profile 'was removed'.
- A models command whose profiles do not resolve reports it with
log.error and exit code 1, as env list and mcp list do, instead of
throwing.
* fix(entries): keep 0.25 files that repeat a name under different roles: working (#707)
0.25.0 let hooks.yaml and mcp.yaml repeat a hook or server name under
different roles:, delivering every copy that passed the role filter (MCP
kept the last). The namespace resolver treated that as a duplicate, so a
member holding both roles, or a role-less member in a team with
projects.yaml, stopped receiving hooks or MCP entirely. During the
roles: deprecation window such a repeat is delivered as in 0.25; a name
repeated without roles: on every copy is still a duplicate.
Also restores the test that the role filter runs before
requireTeamScripts, so the transparency print lists only what will run.
* feat(doctor): say where each entry type's entries come from (#707)
The spec asks doctor, like status and the list commands, to show each
entry's namespace; doctor listed overrides only. For env, hooks, MCP and
models, a namespace contributing any entry now adds a note counting the
entries by origin, 'env: 3 received here (2 root, 1 checkout)', from the
describeOrigins that status uses.
* test(pull): env and hooks conflicts between two namespaces, and builtin: in a namespace file (#707)
Seam 1 asks every type to show, through pull, that two active namespaces
defining one name keep the installed state and name both files. Env and
hooks were covered only through doctor and the handler; so was the
warning for builtin: in a namespace hooks file.
* docs(skill-data): hooks and MCP edits are published with git, not teamai push (#707)
manage-admin.md told admins to publish hooks/MCP file edits with
teamai push, which sweeps only rules/, env/ and .codebuddy-plugin/, so
an agent following it would push nothing. It now says to commit and
push the file with git, as the usage guide does.
* refactor(models): read switched agents without a cast (#707)
* docs(changelog): doctor counts entries per namespace; inject fails on unresolved entries (#707)
* refactor(pull): drop imports the resolver move left unused (#707)
* fix(hooks): install the built-in hooks when the team hooks do not resolve (#707)
A first init or bootstrap whose team hooks did not resolve (a broken
namespace file, a clash, a duplicate id) installed no built-in hook, so
the session-start pull that heals the member never ran. Installed team
hooks are still kept; the built-in hooks are now installed where missing,
with the root file's builtin: overrides whenever hooks/hooks.yaml parses,
and with their defaults only in a tool with no teamai hook when it does
not. init and bootstrap say that the team hooks were not installed.
* fix(manifest): keep an unknown resources: key when roles and projects save (#707)
zod stripped the key this CLI only warns about, so a projects or roles
command run on this version deleted a newer CLI's type from the team
repo for everyone.
* fix(docs): withdraw a copy the team edited after delivery, not only an unchanged one (#707)
Withdrawing an inactive docs namespace compared the local copy with the
current team file only. A doc the team changed after the member received
it was then kept forever with a false 'you edited it' line. A copy equal
to an earlier team commit is what the mirror delivered, so it goes too.
* fix(rules): withdraw a replaced root rule edited in the same push, name a kept copy (#707)
In the JoyCode, OMP, Pi and Copilot rule dirs, a replaced root rule's
copy was removed only while it matched the current root rule. When the
admin edited the root rule and added its namespace override in one push,
the member's unedited copy stayed loaded beside the override, silently.
It is now also compared with the render at the last pull, and a copy
that is kept is named with the fix.
* fix(doctor): fail a check when team hooks or model profiles do not resolve (#707)
teamai status counts such a type as 0 and says to run doctor, but doctor
had failing checks only for env and MCP, so a duplicate hook id or a
two-namespace clash showed nothing there.
* fix(env): warn when --role names a namespace nothing declares (#707)
env add/remove --role <ns> wrote env/<ns>/env.yaml for a namespace no
role or project lists under resources.env, so the variable reached
nobody and nothing said so. The same applies to remove mcp --role.
* fix(env): find a changed namespace env file whose name is not ASCII on push (#707)
git ls-files quotes such a path by default, so it never matched the name
on disk and push skipped the change.
* fix(entries): an active env, hooks, MCP or models file that cannot be read stops the type (#707)
The readers folded every read error into 'file does not exist', so an
unreadable namespace file silently delivered the root entry in place of
its override. Only ENOENT is absence now; any other error is a broken
file, like one that does not parse.
* fix(entries): match env, hooks, MCP and models namespace dirs case-folded, as docs does (#707)
A declared namespace was joined onto the path as written, so with
env: [checkout] and a directory env/Checkout/, macOS and Windows members
got the override and Linux members the root value.
* fix(doctor): split the legacy claudemd paths on '/', not path.sep (#707)
listFilesRecursive always joins with '/', so on Windows every path was
one segment and a claudemd/<ns>/x.md beside claudemd/x.md was never
reported.
* fix(recall): index the rules pull delivers, not the whole rules/ tree (#707)
A namespace rule replaces the root rule of its name, but recall, contribute
and pull indexed every file under rules/: the replaced root rule and the rules
of inactive namespaces came back from recall. Index the resolved rule set, as
docs and skills already do.
* fix(entries): write a namespace file into the directory pull reads it from (#707)
Pull matches a declared namespace to its directory case-folded, but --role and
--project returned the spelling typed. On a case-sensitive filesystem
`env add --project checkout` created env/checkout/, which shadowed
env/Checkout/ and dropped its variables from delivery.
* fix(remove): remove no MCP server by a bare name while an MCP file does not parse (#707)
The team scan skips a file that does not parse. With mcp/mcp.yaml broken,
`remove mcp db` took the one readable checkout/db as the target and removed
it. Refuse and name the file unless the readable root defines the name.
* docs: rules in recall, namespace writes and remove mcp on a broken file (#707)
* fix(remove): say the MCP file --role or --project names does not parse, not that the name is missing (#707)
The team scan skips a file that does not parse, so `remove mcp db --project
checkout` with a broken mcp/checkout/mcp.yaml reported "Not found". Name the
file and remove nothing.
* fix(skills): remove a leftover of another skill version only when it matches that version (#707)
Install removed any installed file at a path another team version of the skill
has, by path alone. A file a member added under that name, e.g. README.md
beside a namespace they never had, was deleted on every pull. Remove it only
when it is byte for byte that version's file; keep any other and name it.
* fix(env): edit no env file that does not parse, and no --project target after a failed refresh (#707)
env add and env remove read the target through parseEnvYaml, which answers an
empty list for a file that does not parse, then wrote that back: every
variable the file had was replaced. They now refuse and name the file.
--project resolves through manifest/projects.yaml. After a failed pull that
copy may be stale and name a namespace the project no longer uses, whose file
push would publish, so --project now changes nothing then. The root file and
--role do not depend on the manifest and still only warn.
* fix(pull): let no unusable namespace item replace the root one (#707)
A skill directory without SKILL.md replaced the root skill of its name:
install overlaid it and removed the installed SKILL.md as the other version's
leftover, while pull still counted the skill as synced. Such a directory is
no longer a skill; pull names it and keeps delivering the root one.
An agent file that does not parse delivers nothing, yet it still replaced the
root agent, and cleanup removed the unchanged root copy because no active
destination held that stem. The root agent now stays while its replacement
cannot be read or parsed.
* fix(push): take no namespace directory without SKILL.md for a member's skill (#707)
Pull stopped delivering such a directory in
|
||
|
|
ca6e51251f |
feat(skill): serve builtin skill content from the CLI, deploy a discovery stub (#699)
* feat(skill): serve packaged skill content from the CLI
Add `teamai skill get <names...> [--full] [--all]` and `teamai skill path
[name]`, so an agent can read built-in skill content that always matches the
installed CLI version instead of a copy deployed into its skills directory.
`get` prints SKILL.md byte for byte, frontmatter included, with {SKILL_DIR}
resolved to the absolute packaged directory so documented script invocations
run as-is. `--full` appends references/ and templates/, walked recursively and
sorted by relative path, because our references nest one level deeper than the
flat layout agent-browser assumes.
Content goes to stdout and every diagnostic to stderr, so the output stays
byte-exact when piped. An unknown flag warns and continues; an unknown name is
fatal, since acting on the wrong skill is worse than a retry.
`skill list` gains the served catalog and `--json`; `skill show` resolves
packaged skills before the installed-agent fallback, which is what keeps it
working once the deployed unit becomes a stub. Legacy directory names resolve
as aliases.
Refs #678
* refactor(skills): move content to skill-data and deploy a single stub
Agents now receive one file: `skills/teamai/SKILL.md`, a discovery stub of
about 2 KB whose description carries the triggers of every workflow and whose
body holds the commands that load them. The workflow content moves to
skill-data/{core,share,wiki}, which is never deployed and is printed by
`teamai skill get`.
Before this, `deployBuiltinSkills` copied three whole trees — 176 KB — into
every installed agent on every pull, so the text an agent read could disagree
with the CLI it documented until the member ran a pull, and a machine with ten
agents held ten copies. skills/ keeps its meaning ("everything here is
deployed"), which is what lets BUILTIN_SKILL_NAMES collapse to one name.
The stub is copied verbatim: no ensureSkillFrontmatter on the way out, so a
deployed copy that differs from the packaged one is a bug rather than a
variant. Recall no longer gates deployment, since the stub routes to every
workflow; the run-time gate for share lands with the pruning pass.
Uninstall learns the legacy directory names, which it would otherwise leave
behind on every machine that upgraded.
"skill-data" is added to package.json files, with a test that asserts it
through `npm pack`: without that entry every test still passes against the
repo and `skill get` serves nothing once installed from the registry.
Refs #678
* fix(skills): repair stale commands, broken refs and frontmatter
An audit of the three builtin skills found 60 defects. This fixes the ones that
survive the move to skill-data, and splits the two skills that were carrying
more than one job.
Stale CLI surface. The wiki skill advertised `teamai extract graph`, a command
that has never existed. The hand-written "ground truth" cheat sheet in the
teamai skill omitted 19 real commands while telling the agent that anything
missing from it could be checked with `--help` — which fails for the flags
`--help` hides. The cheat sheet is replaced by
`skill-data/core/references/commands.md`, rendered from the CLI's own command
table, with hidden flags marked as such. Two tests guard it: one regenerates
the file and diffs, the other resolves every `teamai …` string written anywhere
in skill-data against the command table and fails on an unknown command or
flag. That second test is the one that would have caught
|
||
|
|
e7994db3ca |
chore(deps): upgrade high/critical severity dependencies (#514)
Resolve all 10 Critical+High Dependabot alerts: - smol-toml ^1.3.1 -> ^1.7.1 (runtime, CVE-2026-85730) - js-yaml override ^3.15.1 -> ^3.15.2 (runtime, CVE-2026-84375) - vitest / @vitest/coverage-v8 2.x -> 3.2.7 (clears CVE-2026-47429; stays on 3.x) - override brace-expansion 1.1.18/2.1.4/5.0.9, nanoid 3.3.19, postcss 8.5.28, picomatch 4.0.7 npm audit: critical/high now 0. build + tsc + full vitest suite (3006 tests) pass. |
||
|
|
851d5f2600 |
docs: align README and usage guide with TeamAI product architecture
Reframe public docs around Team Execution × Team Context × Team Improvement, and add vision.md as the longer product write-up. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
40837dc7f3 | fix: harden team package orchestration | ||
|
|
cce6e81a2c |
fix(packages): address install review findings
Keep package acknowledgement and version updates accurate, isolate declaration validation, and harden package setup across hooks, scopes, and Windows. |
||
|
|
fd9f3f8dca |
chore(release): 0.22.0
Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
d92fb4d237 |
chore(release): 0.22.0-beta.5
Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
78063e7f83 |
chore(release): 0.22.0-beta.4
Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
633f67213b | chore(release): 0.22.0-beta.3 | ||
|
|
915fec98f0 | chore(release): 0.22.0-beta.2 (#382) | ||
|
|
db3aa2c841 | chore(release): 0.22.0-beta.1 (#379) | ||
|
|
6e29f84b29 |
feat(wiki-engine): add WASM tree-sitter AST track for code knowledge graph (#304)
* feat(wiki-engine): add WASM tree-sitter AST track for code knowledge graph
The code knowledge graph extractors were purely regex/line-based, which
produced false-positive dependency edges (path-substring matching) and had
no notion of call relationships. This adds a real AST track using
web-tree-sitter (pure-WASM, no native toolchain) for TypeScript/JavaScript,
Python, and Go, ported from the team-wiki reference implementation.
- New ast/ module: WASM parser registry (async one-time init), tree-sitter
queries, symbol/import/call-site walk, import & call resolvers, and
fact/edge adapters. Emits precise file-to-file DEPENDS_ON / REFERENCES
edges tagged source:"code-ast" with confidence weights.
- Dual-track: runs alongside the regex heuristic track (which still covers
Java/Rust/config); AST facts win on merge. Falls back to heuristic-only
and records an AST_UNAVAILABLE gap when the runtime is unavailable or
TEAMAI_SKIP_AST=1.
- code-graph: AST relation facts build precise edges instead of being
re-fuzzed through the path-substring matcher.
- enrich: manifest edges now preserve real AST relation/source provenance
(deterministic rank-based merge) instead of hardcoding DEPENDS_ON /
code-heuristic.
- Pinned web-tree-sitter@0.25.10 + tree-sitter-wasms@0.1.13 (only ABI-14
compatible pair; 0.26 rejects these grammars).
- Docs (README + usage-guide, EN/zh-CN) and unit tests added.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(wiki-engine): address AST track code-review findings
- walk.ts: free the parsed tree-sitter Tree via try/finally { tree.delete() }
to release WASM off-heap memory; JS GC does not reclaim it, so large repos
would otherwise grow the emscripten heap unbounded.
- call-resolver.ts: memoize import bindings per source file in resolveCallSites
(was rebuilt for every call site — O(callSites × imports)).
- import-bindings.ts: detect Python exports via module-level class/function
definitions instead of the non-existent "__export__" node type, so Python
symbol-level call resolution works (was always exported=false).
- merge-edges.ts / import-resolver.ts: drop unused findConflictingEdges,
EdgeConflict, and clearTsconfigCache (speculative dead code).
- walk.ts: simplify a no-op ternary on the call receiver.
- tests: add a Python module-level-export regression case.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(wiki-engine): resolve Python dotted imports and TS implements edges
Two AST-resolver precision improvements from PR review follow-up:
- Python multi-segment imports: `from a.b.c import x` now maps the dotted
module to a nested path (a/b/c.py or a/b/c/__init__.py) instead of only
resolving single-segment sibling imports.
- TS/TSX IMPLEMENTS edges: a class's `implements` clause now produces
IMPLEMENTS edges (source:"code-ast") to the interface's defining file,
resolved via same-file interface symbols or imported bindings. A separate
query pattern avoids the capture-map collision when a class implements
multiple interfaces; names that resolve to neither (ambient/global types)
are skipped rather than emitting a spurious edge.
Adds tests for both; Go cross-package module resolution remains a known
follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: mention IMPLEMENTS edges in AST track description
Keep README and usage-guide (EN/zh-CN) in sync with the new TS implements
edge support.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(import): serialize team-repo write phase to stop batch races dropping AST edges
Batch imports (--from-repo-list and --from-org, which run importFromRepo
concurrently at concurrency>=2) were losing AST edges: the final per-repo
graph-index.json committed to the team repo contained only code-heuristic
edges, while single --from-repo and concurrency=1 produced the correct
code-ast edges.
Root cause: each concurrent importFromRepo writes into the shared
teamwikiRoot (per-repo graph copy, facts/interfaces caches, source-manifest,
router/index, and reconcileKnowledge's global graph). Those are
read-modify-write operations on shared files, so parallel repos clobbered
each other's artifacts.
Fix: a module-level promise-chain mutex. The clone + extractCodebase phase
(the expensive part, which writes only to the per-repo cache dir) stays
parallel; only the team-repo write phase is serialized. A repo acquires the
lock after extract and releases it in the existing finally, so it is
exception-safe and never deadlocks. Verified: with the fix, concurrency=3
batch import produces the same code-ast edge counts as concurrency=1.
Adds unit tests asserting the mutex's mutual-exclusion, re-acquire, and FIFO
contract.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Revert "fix(import): serialize team-repo write phase to stop batch races dropping AST edges"
This reverts commit
|
||
|
|
0e0051fdd3 | 0.21.0 | ||
|
|
c3782a3e21 |
fix(opencode): Fix invalid agent configuration generated when recall is enabled (#344)
fix(opencode): Fix invalid agent configuration generated when recall is enabled fix(recall): Clean up Codex native proxy files --------- Co-authored-by: baijinrong <baijinrong@kuaishou.com> |
||
|
|
6bb1991277 |
0.21.0-beta.10
Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
a08308f487 |
0.21.0-beta.9
Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
003bb712ec |
0.21.0-beta.8
Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
4197c4f0c4 | 0.21.0-beta.3 | ||
|
|
3c9339bdea | 0.21.0-beta.2 | ||
|
|
8650d58d1c | 0.21.0-beta.1 | ||
|
|
02f251b7bf | chore(release): 0.21.0-beta.0 | ||
|
|
c89e4718c2 |
fix(deps): patch runtime security advisories in simple-git and js-yaml (#316)
Bump the two production dependencies flagged by Dependabot as runtime-scope vulnerabilities: - simple-git ^3.27.0 -> ^3.36.0 (installed 3.35.2 -> 3.36.0) fixes RCE advisory GHSA (Dependabot #6, high) - js-yaml -> ^3.15.1 via overrides (pulled transitively by gray-matter, was 3.14.2) fixes quadratic-CPU DoS via merge-key chains (Dependabot #16, high) `npm audit --omit=dev` now reports 0 vulnerabilities. Remaining open alerts are all devDependency-scoped (build/test toolchain) and do not ship in the published package. |
||
|
|
6abfc69f45 | 0.20.0 | ||
|
|
6d44a47a6a | 0.19.0 | ||
|
|
9bebdf3272 | 0.18.0 | ||
|
|
8eb79354c7 | 0.17.7 | ||
|
|
3d0a666f6c | 0.17.6 | ||
|
|
653281213b | 0.17.4 | ||
|
|
7d323f0ade | chore(release): 0.17.3 | ||
|
|
95c77ed051 |
chore(release): 0.17.2 (#159)
Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
04eef5a947 |
chore(release): 0.17.1 (#157)
Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
e922992f85 |
chore(release): 0.17.0 (#148)
Co-authored-by: jeffyxu <jeffyxu@tencent.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
44e0bcb2ef |
feat(import): add listr2 progress bar and English CLI output (#117)
- Add listr2 dependency for structured multi-step progress display - Wrap --from-repo, --from-repo-list, --from-mr, --from-org with listr2 task lists showing step-by-step progress with timers - Translate ~80 Chinese log/spinner/error strings to English across import-repo.ts, import.ts, import-mr.ts, import-iwiki.ts, import-org.ts, import-repo-list.ts, codebase-extract.ts, deep-enrich.ts, import-local.ts - Preserve Chinese in AI prompts and generated document content - Preserve Chinese in code comments (only user-facing output translated) Co-authored-by: jaelgeng <jaelgeng@tencent.com> |
||
|
|
8825589d0f |
feat: git-free HTTP team repo + hooks-driven agent status reporting (#68)
* feat: HTTP team repo + hooks-based agent status reporting (issue #1) 实现 issue #1 评审通过的两套方案,共享同一套 skill 分发原语。 方案一(git-free HTTP 团队仓库 + skill 只读拉取): - source-http.ts: GET /repo 物化(files[] 内联 + commands[] 走共享执行器), 路径穿越防护,version 作为增量缓存 key - init --http <url>: 只读消费者 onboarding(只需 API key,跳过 git/clone/member/reviewer) - pull.ts: 抽象 refreshTeamRepo() 收口 git/http 两种刷新,其余管线原样复用 - read-only.ts: http kind 下 push/contribute/remove 明确拒绝 方案二(hooks 驱动的 agent 状态上报): - machine-id.ts: 跨平台 machine_id(macOS ioreg / Windows reg / Linux machine-id) + local_agent_id 派生(install_path 仅本地哈希,不上报) - status-report.ts: report/sync/ack 三接口 + 离线队列 + clawpro/local 来源区分; 接口路径走可覆盖的内部映射(默认 iWiki 契约,TEAMAI_REPORT_PATHS 可覆盖) - 挂到既有 hook dispatch: session-start→report+sync,prompt-submit→sync - openclaw-hooks.ts: WorkBuddy(龙虾系)HOOK.md + handler.ts 注入适配器 共享地基: - skill-command.ts: executeSkillCommand(fflate 解压 zip,含 SKILL.md 校验、 路径穿越防护、SMH 直连下载),push/pull 两条路径共用 - api-key.ts: 统一 Bearer 凭证解析/保存(0600,不入 config/不上报) - teamai login <key> 命令 测试: 本地实现三接口 mock HTTP 服务(进程内 helper + scripts/mock-teamai-server.mjs 独立可运行版),单测 + 集成端到端共 49 个新用例全部通过。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit e8e50532024bfccf83a1b968cfc7d5a0de4472e3) * fix: align local-agent reporter with updated backend contract 后端契约调整(对齐 clawpro https://5hborhrw.cvmopenclaw.site): 1. 接口名去掉 v1:/api/v1/local-agent/* → /api/local-agent/* 2. ack 的 command id 从 path 移到 request body,类型 int: POST /api/local-agent/commands/ack,body { id: <int>, status, error } - status-report.ts: EndpointMap.ack 由路径构造函数改为固定路径字符串; 默认路径去 v1;ack body 带 int id。TEAMAI_REPORT_PATHS 覆盖保留。 - skill-command.ts: SkillCommand.id string → number。 - mock-server.ts / mock-teamai-server.mjs / 单测同步更新。 端到端验证(真实后端):report/sync → 200,ack 路由按 int body id 校验; 完整 install→ack 闭环(mock)通过。tsc 通过,vitest 1454 passed 无回归。 Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit fe5d649f4f29a0e70996a011ee670bb32ec7ca27) * fix: wire WorkBuddy via settings.json Claude hooks (not OpenClaw) Real-device verification (WorkBuddy 5.2.0) shows WorkBuddy embeds the CodeBuddy CLI engine and reads Claude-format hooks from ~/.workbuddy/settings.json: SessionStart / UserPromptSubmit / PostToolUse all fire with PascalCase event names and CLI-style tool names (tool_name="Bash"), identical to codebuddy. MR 191 originally (wrongly) assumed WorkBuddy used the OpenClaw HOOK.md engine, so teamai never wired it (0 workbuddy events on a real machine). Fix: - types.ts: toolPaths.workbuddy now carries settings: '.workbuddy/settings.json' → routes through the Claude-format injection path like codebuddy. - hooks.ts: drop workbuddy from OPENCLAW_TOOLS (kept for the still-unverified openclaw/qclaw/easyclaw/autoclaw variants). - openclaw-hooks.ts: clarify it's no longer for WorkBuddy; default tool=openclaw. - tests updated to assert workbuddy → settings.json hooks (--tool workbuddy), OpenClaw HOOK.md path only for openclaw. Verified end-to-end on the real machine: `teamai hooks inject` writes the workbuddy hook block (preserving claw/enabledPlugins/sandbox); real WorkBuddy fires the hooks; `hook-dispatch --tool workbuddy` records tool=workbuddy in the dashboard. tsc OK, vitest 110 files / 1466 passed. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit d1ec907433177e3ac76ed81867fec928be647293) * fix: teamai uninstall also removes OpenClaw HOOK.md hooks MR 191 added injectOpenClawHooks but uninstall only removed hooks from tools with a `settings` path, so OpenClaw-style HOOK.md/handler.ts dirs (~/.<tool>/hooks/teamai-status-report) leaked on uninstall. - uninstall.ts: discover + remove OpenClaw hook dirs for settings-less tools (mirrors the inject path); listed in the removal summary. - Added regression test asserting the OpenClaw HOOK.md dir is removed. Verified end-to-end: with workbuddy now settings-based, `uninstall` strips its teamai hooks while preserving claw/enabledPlugins/sandbox; OpenClaw HOOK.md dirs are removed (previously leaked); real-machine dry-run lists ~/.workbuddy/ settings.json. tsc OK, vitest 110 files / 1467 passed. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 4a9a2361c55292547dd033ade98825ac3083811a) * feat: tolerate missing /repo in HTTP init (reporting-only mode) A clawpro-style backend may ship the status-reporter endpoints before the team-repo /repo endpoint exists. Previously `teamai init --http <url>` hard- failed on such endpoints (/repo → 404 or 200 HTML), so there was no first-class way to configure endpoint+key for reporting. - source-http.ts: fetchRepoSnapshot now distinguishes "/repo not live yet" (404 or non-JSON 200 body) by throwing RepoNotAvailableError; auth (401/403) and other errors stay hard failures. - init.ts: on RepoNotAvailableError, init --http enters reporting-only mode — writes a minimal local teamai.yaml (default toolPaths), saves the http config (endpoint+key), injects hooks, and prints a clear notice. When /repo later comes online, a normal pull materializes skills with no re-init. - pull.ts: refreshTeamRepo swallows RepoNotAvailableError (reporting-only) so every session doesn't error while /repo is absent. - tests: RepoNotAvailableError classification (404 / non-JSON / 500). Verified e2e against the real reporter-only backend: `teamai login <key>` + `teamai init --http <url>` now succeeds (reporting-only), reporter report/sync hit the backend (200, no offline queue), and pull no longer hard-fails. tsc OK, vitest 110 files / 1470 passed. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 75b03c633a731f302375c764df970a5b84dba00d) * refactor: fold `teamai login` into `init --http --token` (remove login command) Reduces command surface and puts endpoint + key in one place (addresses the "why are login and endpoint separate" awkwardness). The `login` command was only introduced in this unreleased HTTP/reporter feature set, so removing it is not a breaking change for released users. - index.ts: remove `login` command; add `--token <key>` to `init`. - init.ts: initHttp persists --token via saveApiKey (0600) before resolving; still falls back to TEAMAI_API_TOKEN / existing apikey file. - update "run `teamai login`" hints in source-http.ts / pull.ts / api-key.ts. One-command setup now: teamai init --http <url> --token <key> Verified e2e against the real reporter-only backend: single command saves the key, configures the http endpoint (reporting-only), injects hooks; reporter report/sync hit the backend (200). `login` no longer appears in --help. tsc OK, vitest 110 files / 1470 passed. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit e98b48f7fc63eddc755fa9ef04260ad9055c49d3) * fix: skip git usage auto-report for HTTP consumers (no .git → noisy ERROR) In HTTP team-repo mode the local team-repo path is not a git checkout, so pull's Step 5 usage auto-report (reportUsageToTeam, git-based) failed every session with `[ERROR] Auto-report skipped: fatal: not a git repository`. HTTP consumers are read-only and have no git remote to report to, so skip the step entirely when repo.kind === 'http'. Verified: `teamai pull` in HTTP reporting-only mode now produces no Auto-report error. tsc OK, vitest 110 files / 1470 passed. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 388078d0c587c03602b93a5635e193961e84e592) * feat: log skill-command execution in the reporter (observability) Skill install/uninstall driven by `sync` commands previously ran silently — on failure the reporter only ack'd `failed` with no local log, making it impossible to see why a dispatched skill (e.g. fd-find) didn't install. Now: - log.debug the number of commands returned by sync - log.debug each command success - log.error each command FAILURE with the underlying error message No behavior change beyond logging. tsc OK, vitest 110 files / 1470 passed. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 9bbabfcc0ef467b1addf605a29a2754632c455f4) * chore: drop accidentally committed .teamai/domains.yaml (test artifact) Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 882698389411271ca359725b17e61907143c12ad) * feat: surface skill download URL + server error body in reporter logs A failed install previously logged only "download failed: HTTP 409", giving no clue what was being fetched. Now downloadZip logs the signed download_url before fetching and includes the server response body + URL in the thrown error (which lands in debug.log and the ack error field). The reporter also logs each sync command (incl. download_url) and renders empty skill_version as "?" instead of a bare "@". Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 2a58ede7abf0e411ad61474adc0b65d21887f713) * feat: log every reporter run + report/sync/ack outcome Previously a successful report or sync produced no log line, so there was no way to tell whether a SessionStart/UserPromptSubmit hook actually fired or whether sync ran ("seems sync never triggered"). Now each invocation logs run (agent/phase/id/endpoint) and the OK/FAILED outcome of report, sync (with command count), and each ack. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 49ac63953c9d6e33e8f9998a2be86ea91da2e00e) * feat: skip team-repo built-in skills in HTTP reporting-only mode teamai-share-learnings and teamai-wiki both write to the team repo, so in reporting-only HTTP mode (no /repo) they are non-functional. refreshTeamRepo now reports reportingOnly, which pull threads into deployBuiltinSkills to skip injecting them. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit d5c40475ba99078174ba553719b707e6a0d88ccc) * fix: accept flat skill zips (SKILL.md at root), not just <slug>/SKILL.md The clawpro/skillhub backend packages skills as a flat zip (SKILL.md + _meta.json at the root, e.g. find-skills-skill), but installSkillZip required a top-level <slug>/ directory and failed with "skill package missing <slug>/SKILL.md". installSkillZip now resolves the SKILL.md location across three layouts (nested-by-slug, flat root, nested-other-name) and installs the contents into <skillsDir>/<slug>/. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit fbdf643c79c42613eb52fb7c383fddeb893b9b83) * fix: use reconcileTeamHooksForConfig for HTTP init hook injection Reconcile the issue-#1 HTTP init path onto main's unified-hooks (#65) architecture: the old `injectHooksToAllTools(toolPaths, baseDir)` entry no longer exists, so the HTTP consumer now injects hooks via the same authoritative `reconcileTeamHooksForConfig` path the git init uses. Fixes the tsc TS2304 (injectHooksToAllTools / resolveBaseDir not found) seen in CI. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: document git-free HTTP team repo + agent status reporting Add a "Read-only consumers (HTTP team repo, no git)" quick-start subsection to both README.md and README.zh-CN.md, covering `init --http --token`, reporting-only fallback, hooks-driven status reporting, and the local-only hashing of install path / machine id. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: document the HTTP contract (endpoints, /repo schema, env knobs) Add a collapsible "HTTP contract" block to both READMEs spelling out what a --http backend must serve: the fixed GET /repo snapshot shape, the three (overridable) local-agent reporter endpoints, the signed download_url + accepted zip layouts, and the env vars that make paths/hosts/agents configurable. Clarifies what is fixed vs configurable. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(reporter): drop per-skill `source` tag and clawpro bookkeeping The report payload no longer tags each skill `source: clawpro|local`. With `source` gone, the entire clawpro-skills.json bookkeeping (getClawproSlugs / recordClawproSlug / clawproRecordPath) had no remaining consumer, so it is removed. `scanReportableSkills(skillsDir)` now just lists every skill found in the agent's own skills dir. Tests updated accordingly. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
ba149c8447 | 0.16.8 | ||
|
|
583943b671 |
Merge origin/master (0.16.7) into GitHub line: reconcile hook architectures
Reconciles the TGit lineage (published as npm 0.16.7) with the GitHub lineage (#28-#30: subagent push, knowledge-base import, contribute-check Phase 2) by adopting the unified hook-dispatch architecture and porting the GitHub-only hooks onto it: - todowrite-hint: registered as a post-tool-use/TodoWrite dispatch handler; hooks.ts now emits a `hook-dispatch post-tool-use --matcher TodoWrite` entry. - mr-hint: registered as a session-start dispatch handler; mr-hint.ts gains a stdin-free computeMrHintOutput() core (the dispatcher consumes stdin once). - contribute-check handler now forwards cwd for #30's knowledge-gap logic. - Legacy per-command hooks (incl. todowrite-hint, mr-hint) added to the migration cleanup list. Tests updated for the merged dispatch counts (PostToolUse 6->7, total 9->10). All unit + e2e tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
aff668899e | 0.16.7 | ||
|
|
7fd9f436a4 |
Phase 0+Phase 1+P4.4: 实现了subagent推送,检索subagent,初始化知识库导入以及MR自动learnings&codebase更新功能(团队级别codebase文档功能有待完善) (#28)
* docs:知识库飞轮系统设计roadmap文档
* feat: phase 1 - agents support, multi-index search, recall subagent & todowrite hint
- Add agents resource type (pull/push/remove support)
- Add builtin-agents with teamai-recall agent
- Add multi-source search index (rules + skills + agents)
- Add todowrite hint injection for recall subagent
- Add phase1 e2e tests and unit tests for new features
- Update README (zh-CN + en) with agents documentation
* docs: update roadmap文档
* feat(search): P1.4 domain inference + search weighting
Introduce KnowledgeDomain (technical/ops/support/neutral) inferred from
frontmatter > tags > path > type fallback. Apply per-domain score
multipliers at search time (technical ×1.0, neutral ×0.85, ops ×0.5,
support ×0.3) plus a skills/rules type bonus (×1.1).
Bump SEARCH_INDEX_VERSION 2→3; legacy v2 indexes auto-rebuild on next pull.
--other=P1.4 domain inference and search weighting
* test(validation): add Phase 1 E2E suite and acceptance report
Move phase1-e2e.test.ts to validation/ alongside the acceptance report,
update import paths and vitest.e2e.config.ts to pick up the new location.
Fix pre-existing E2E isolation bug: loadStateForScope mock used
mockResolvedValue (shared object reference), causing test-1 to mutate
state.lastPullRev and trigger the rev-based early-exit in test-2/3.
Switch to mockImplementation(() => ({ lastPull: null })) so each call
gets a fresh object. All 5 E2E tests now pass.
--other=Phase 1 validation
* docs: update roadmap — add P4.6 learning promotion mechanism
Learning entries that accumulate confidence ≥ 0.90 (5+ upvotes, 2+
contributors, 14+ days old) are prompted for promotion to docs/skills/rules
based on content type, regardless of origin or domain. Add P4.6 step to
Phase 4, dependency graph, implementation detail, work-estimate table,
and architecture overview diagram.
--other=roadmap update
* docs(validation): update phase1 report to reflect E2E bug fix
All 5 E2E tests now pass. Update P1.2 status to ✅, remove E2E
failure notes and known issues, set pass rate to 100%.
--other=phase1 report update
* docs(validation): add runtime evidence appendix to phase1 report
Append Appendix A1-A4 with captured outputs from demo-phase1.test.ts:
agents sync paths, CLAUDE.md full content after injection,
search-index.json entry listing (4 types covered), and recall("api")
full STDOUT including envelope markers and domain-weighted scores.
Knowledge content in A3/A4 is anonymised (titles, authors, file paths
replaced with generic placeholders).
Also commit demo-phase1.test.ts as the reproducible evidence runner.
--other=phase1 report runtime evidence
* feat(search): query-aware domain weights + IDF scoring (v4 index)
改动 A — 查询感知 domain 权重:
将静态一维 DOMAIN_WEIGHT 改为二维查询-文档权重矩阵,新增
inferQueryDomain() 从查询 token 推断查询域。搜索 k8s/deploy 等
ops 相关问题时,ops 条目不再被打五折,technical 查询行为与原来一致。
改动 B — IDF 降权:
buildIndex() 末尾计算 df(文档频率)map 并写入索引;search() 中
为每个 token 匹配乘以 log((N+1)/(df+1))+1 的 IDF 权重,高频通用词
(api、deploy、error)自动降权,低频专有词(deepgemm、mooncake)
权重保持不变。
索引版本 3 → 4;isLegacyIndex() 新增 !index.df 判断触发重建。
旧 v3 索引在下次 teamai pull 时自动重建,search() 对无 df 字段的
旧索引降级为 idf=1.0,不报错。
--other=search quality improvements
* feat(import): add teamai import command — Phase 0 cold-start + P4.4 MR pipeline
## 新增命令:teamai import
支持五种知识来源:
- --dir <path>:扫描本地目录,AI 分类为 rule/doc/learning
- --from-claude:迁移 ~/.claude/rules 等 AI 工具规则目录
- --workspace:基于当前 git 仓库生成 codebase.md
- --from-mr <url>:从已合并 MR 提炼 learning + codebase 更新建议(P4.4)
- --from-iwiki <id/url>:从 iWiki Space 批量导入文档
## 新增核心模块
- src/utils/ai-client.ts:claude -p 子进程封装(并发 ≤ 3,60s 超时)
- src/utils/dedup.ts:Jaccard 相似度重复检测(14 天窗口,≥ 60% 标记 superseded)
- src/utils/iwiki-client.ts:iWiki MCP HTTP 客户端(JSON-RPC 2.0,零外部依赖)
- src/import-local.ts:本地文件扫描/AI 分类/交互确认/推送
- src/import-mr.ts:MR 三层解析/双路 AI 提炼/dedup/推送
- src/import-iwiki.ts:iWiki 导入(复用 import-local.ts 基础设施)
- src/codebase.ts:codebase.md 生成/增量更新
## 扩展现有接口
- providers/types.ts:GitProvider 新增可选 fetchMergeRequest() 方法
- providers/github/mr-fetch.ts:gh pr view 实现
- providers/tgit/mr-fetch.ts:gf mr 实现
- types.ts:新增 MRData/ClassifiedItem/LearningDraft/CodebaseSuggestion/ImportSession
## 测试 & 文档
- ai-client.test.ts:5 tests(spawn mock + 并发控制)
- dedup.test.ts:11 tests(关键词提取 + Jaccard + 文件扫描)
- validation/phase0-p44-acceptance-report-public.md:Phase 0 + P4.4 验收报告
--story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线
* fix(import): support claude-internal CLI + gh REST API fallback + real PR demo
## ai-client.ts
- detectClaudeCli():按 claude → claude-internal 优先级探测,结果缓存,
进程内只探测一次,两者均不可用时给出清晰错误提示
- DEFAULT_TIMEOUT_MS:60s → 180s,适应大 diff 下 AI 提炼耗时
## providers/github/mr-fetch.ts
- gh CLI 不可用时自动降级到 GitHub REST API(内置 https,零依赖)
- 支持公开仓库无 token 访问;有 GITHUB_TOKEN 环境变量时自动携带
## import-mr.ts
- codebase 建议 JSON 解析:先提取 {…} 块再解析,
兼容 AI 在 JSON 前附加说明文字的输出格式
## index.ts
- import 子命令新增 --all 选项,跳过交互确认
## validation
- phase0-p44-acceptance-report-public.md A4:替换为基于真实 PR #2
的端到端操作记录(真实终端输出 + AI 真实生成的 learning.md
和 codebase-suggestions.json 完整原文)
--story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线
* fix(codebase): require file-path prefix in module descriptions for agent guidance
codebase.ts 和 import-mr.ts 的提示词中"主要模块"格式要求均已更新:
- 之前:**模块名** — 功能说明(AI 可能只写中文名,无路径索引)
- 之后:**文件或目录路径** — 功能说明(明确要求带路径,便于 agent 定位)
codebase.ts: 全量生成 prompt 示例改为 **src/utils/git.ts** — 功能说明
import-mr.ts: codebase 建议 prompt 新增正确/错误示例,强制路径前缀
同步更新验收报告 A4:
- codebase-suggestions.json 从 8 条无路径条目 → 11 条带路径条目(真实重跑输出)
- 更新后 codebase.md 主要模块列表格式与更新前一致
--story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线
* feat(codebase): upgrade workspace + MR prompts to A1-level documentation quality
codebase.ts — gatherRepoContext 扩展:
- 增加 package.json(依赖和 scripts)
- 增加入口文件(src/index.ts)命令注册全文
- 增加类型定义文件(src/types.ts)关键接口
- 文件树深度 maxdepth 3→4,过滤 dist/ worktrees/
- 截断上限:FILE_TREE 3000→5000 字符,DOC 1000→2000 字符
- 新增 META_MAX_CHARS 常量(2500 字符)
codebase.ts — 全量生成 prompt 重写:
- 提供完整 8+ 章节格式骨架(项目概述/技术栈/目录结构/数据配置/
核心数据流/关键接口/配置系统/性能可靠性/测试覆盖/备注)
- 目录结构要求带分组框树形图(┌─ 功能分组 ──┐ 风格)
- 技术栈要求表格含版本信息
- 项目概述要求带 emoji 核心能力 bullet list
- 核心数据流要求带缩进 → 的流程图格式
import-mr.ts — codebase 建议 prompt 升级:
- 新增 existingCodebaseMd 参数,注入现有文档全文作为格式样本
- AI 参考现有文档的分组和粒度生成风格一致的增量条目
import.ts — --from-mr 分支:
- 调用前读取 repoPath/docs/codebase.md 传入 existingCodebaseMd
- 确保 MR 增量更新与初始生成风格一致
- 复用已导入的顶层 fs 模块,删除内联 dynamic import
--story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线
* docs(validation): replace A1+A4 codebase docs with real teamai-cli generated output
A1 附录:替换为 teamai import --workspace 对 upstream/main 真实生成的
codebase.md(含分组框目录树、表格技术栈、emoji 核心能力、流程图数据流)
A4 附录:
- Step 1:更新 codebase-before.md 为同一真实生成版本
- Step 2/3:终端输出更新为 3 条建议(主要模块 7 条/关键路径 4 条/架构决策)
- Step 3:learning.md 更新为最新真实 AI 输出
- Step 4:更新前后对比基于真实文档
- Step 5:飞轮闭环统计数字更新(3 条建议)
--story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线
* feat(mr-hint): add SessionStart hook to hint AI about unimported merged MRs
P4.4 优化:在每次 Session 开始时检测当前 git 仓库的 origin remote,
查询近 7 天内已合入但尚未通过 teamai import 处理的 MR,并通过
additionalContext 提示 AI 在任务完成后建议用户运行 teamai import --from-mr。
核心实现:
- src/mr-hint.ts:新增 mrHint() 入口,支持 TGit REST API 和 GitHub gh CLI
双路查询;per-repo 磁盘缓存(30 天 TTL)避免重复提示相同 MR
- src/hooks.ts:注册 SessionStart hook,更新 TEAMAI_COMMAND_MARKERS
和 TEAMAI_HOOK_SUBCOMMANDS,同步 buildCursorHooks
- src/index.ts:注册 mr-hint 子命令
- 同步修复 hooks.test.ts、usage-tracking.test.ts、doctor.test.ts
中 todowrite-hint 加入后遗留的计数断言
--other=P4.4 MR 合入统一处理流水线(SessionStart 触发提示)
* feat(mr-hint): add GitHub REST API fallback when gh CLI unavailable
gh CLI 不在环境中时自动回退到 GitHub REST API(/repos/.../pulls),
逻辑与 providers/github/mr-fetch.ts 保持一致;
支持公开仓库无 token,有 GITHUB_TOKEN 时自动携带以提升限速上限。
--other=P4.4 MR 合入统一处理流水线(mr-hint GitHub REST fallback)
* docs(validation): update public acceptance report for P4.4 mr-hint trigger mechanism
新增 P4.4 触发机制优化章节,更新附录 A1(基于含 mr-hint 模块的代码库
真实生成),追加附录 A4.2(SessionStart hook 自动感知 merged PR 的真实
运行场景,含 GitHub REST API fallback 演示与幂等性验证)。
--other=P4.4 MR 合入统一处理流水线验收报告更新
* feat(ai-client): support login shell PATH + extend CLI candidates
- 用 bash -lc 包裹探测和调用,解决 ~/.nvm 路径下 CLI 不在 PATH 的问题
- 探测顺序扩展为 claude / claude-internal / codex / codex-internal /
codebuddy / workbuddy / openclaw
- 新增 shellEscape() 避免 prompt 中单引号破坏 shell 命令
- 超时从 180s 降至 120s
- 测试补充 execFileSync mock,修复预存 5 个失败用例
feat(import-mr): interactive codebase review loop + apply suggestions
- 新增 reviewCodebaseSuggestions():AI 实时修订循环,用户输入意见
→ AI 修订 → 再展示,直到 y 确认或 n 跳过
- --output 模式:apply 后写 codebase-after.md(完整 Markdown)
- repoPath 模式:apply 后写回 docs/codebase.md
- 修复 codebase.ts apply prompt,确保输出完整文档而非摘要
feat(import): add --existing-codebase option
allow 用户显式指定 before codebase.md 路径,不依赖团队仓库;
优先级高于从 repoPath/docs/codebase.md 自动读取
--other=P4.4 MR 合入统一处理流水线优化
* docs(validation): update A4 with real before/after codebase demo
用真实运行产物替换 A4 中的 codebase before/after 内容:
- Step 1 before:由 teamai import --workspace 在 PR #2 合入前的代码库生成
- Step 3 suggestions:teamai import --from-mr PR #2 的真实 AI 输出
- Step 4(新增):apply suggestions 后的 codebase-after.md,含 diff 展示
三阶段格式统一,均为同一版本 prompt 的真实运行产物
--other=P4.4 验收报告 A4 codebase before/after 更新
* fix(providers): add TGit REST API fallback + multi-shell CLI detection
- mr-fetch.ts: 新增 fetchTGitMRViaApi(),gf CLI 不可用时自动 fallback
到 git.woa.com REST API(使用 ~/.netrc OAuth token);
diff 获取失败时降级为空字符串而非中断流程
- ai-client.ts: detectClaudeCli() 对每个候选依次尝试
bash -lc → zsh -lc → which,覆盖 fish/CI 容器等非标准 shell 环境
--other=fix-risk-items
* docs: 验收文档typo更正
* fix(ai-client): multi-CLI compat + shell injection hardening
- ai-client.ts: detectClaudeCli 解析 CLI 绝对路径并校验存在性,
spawn 改为直调 absPath + 参数数组,删除 shellEscape 去 shell;
新增 buildCliArgs 区分 codex/codex-internal 用 'exec' 子命令、
其余 CLI 用 '-p',修复 codex 系 CLI 调用失败问题
- providers/tgit/mr-fetch.ts: execSync → execFileSync 数组参数,
消除 mrIid/repoArg 命令注入风险
- mr-hint.ts: TEAMAI_MR_HINT_CWD 增加 path.resolve + statSync
校验,非法路径静默跳过
- ai-client.test.ts: mock 适配新探测语义(command -v 返回路径
+ existsSync=true)
--other=phase0-p44-cli-compat-and-security
* feat(codebase): align with llm-wiki — frontmatter / index / lint / multi-source
参照 docs/llm-wiki.md 的持久化知识库理念,对 codebase 文档生成做四项优化:
- frontmatter:generateCodebaseMd 输出顶部注入 YAML frontmatter
(title / lastUpdated / source / generator / schemaVersion),
支持去重旧 frontmatter,便于跨会话溯源
- 索引体系:新增 generateCodebaseIndex 导出,从 codebase.md 抽取
二级章节 + 一句摘要 + 关键词,输出 codebase-index.md,加速 LLM 跨
会话定位
- 健康检查:新增 lintCodebaseMd 导出,AI 检测矛盾/过时/孤儿/缺失
四类问题,返回 LintReport(含 severity 分级),不修改文档
- 多源聚合:generateCodebaseMd 入参新增 learningsSuggestions 与
learningsDir,gatherLearningsContext 内部函数读取 learnings/*.md
frontmatter tags 做高频统计,融合 P4.4 MR 建议进 prompt
- prompt 模板新增"架构决策与权衡""已知限制与演进方向"两章节
- import.ts workspace 流程串入索引生成 + lint 报告打印
- types.ts 新增 LintIssue / LintReport 接口
- 新增 codebase.test.ts 11 个单元测试,全部通过
--other=phase4-codebase-llm-wiki-alignment
* feat(search): codebase-index.md high-weight + skip codebase.md
- 新增常量 CODEBASE_INDEX_FILENAME / CODEBASE_FULL_FILENAME /
CODEBASE_INDEX_WEIGHT_BOOST(×1.5)
- entryFromMdFile:同目录存在 codebase-index.md 时自动跳过
codebase.md,避免全量文档与索引文件重复命中
- search():codebase-index.md 命中时额外乘以 1.5 权重 boost,
recall 时章节摘要优先返回
- 兼容 subagent 与 fallback recall 两条路径,boost 在本地索引
阶段生效,无额外 AI 调用开销
- 新增 3 个测试用例(跳过逻辑 / 权重 boost / fallback 路径),
search-index.test.ts 共 26 tests 全通过
--other=phase4-codebase-index-search-boost
* docs(validation): refresh A1/A4 with llm-wiki-optimized codebase output
用新版 CLI(llm-wiki 优化后)重新执行 A1/A4,更新公开版验收报告产物:
- codebase-before.md:含 YAML frontmatter、架构决策与权衡、
已知限制与演进方向两新章节
- codebase-index.md:新增章节索引文件(11 行索引表)
- codebase-after.md:PR #2 应用建议后的更新版
- learning.md / codebase-suggestions.json:最新 AI 提炼产物
- 记录实际使用模型:claude-internal v1.1.9(DeepSeek-V3.1-Terminus)
- 保持 tgit → [internal] 等脱敏风格
--other=phase0-p44-acceptance-report-refresh
* docs(validation): replace codebase-after full content with before/after diff
A4 Step 4 中 codebase-after.md 展示方式由全文改为 unified diff,
更直观反映 MR 建议应用后的变更:新增"主要模块"章节(+8 行),
包含 import-local/import-mr/import-iwiki/codebase 四个关键模块说明
--other=phase0-p44-acceptance-report-refresh
* docs(roadmap): add Phase 6 — Phase 5 hardening
Phase 5 shipped the team-level codebase aggregation pipeline; in
shipping it we deliberately deferred several reliability concerns to
keep each step deliverable. Phase 6 captures those deferrals as a
focused hardening pass — no new capability surface, just turning the
Phase 5 deliverables into something safe to run in production
indefinitely.
Six sub-steps, three independent (P6.0 / P6.1 / P6.5) and three
chained (P6.2 → P6.3 → P6.4):
- P6.0 Real TGit listOrgRepos (replace stub)
- P6.1 Cache lifecycle (LRU + size cap + GC command)
- P6.2 Section-level diff with HTML-anchor in-place updates
- P6.3 pending-review CLI (review / apply / reject)
- P6.4 Domain-drift auto-apply workflow
- P6.5 Global codebase doc lint (cross-file consistency)
Appendix C dependency table updated with all six rows.
Phase 5 leftovers explicitly out of scope here (二级业务域 / 跨仓重复
检测 / search-index 联动 / agent 检索效果量化) are listed under the
new "遗留至 Phase 7" block.
--other=phase6-roadmap
* feat(import): Phase 5 — team-level codebase aggregation
Lift teamai-cli's codebase knowledge base from a single-repo, local view
into a team-wide, multi-repo aggregation that can be initialized in one
command, kept in sync incrementally, and audited end-to-end. The work
ships in five sub-steps but is a single coherent feature; merging as one
commit per the project's MR rules.
What's new
==========
P5.0 Business-domain dictionary (src/domains/*)
Zod schema + YAML store + AI batch clustering + single-repo
recommendation + interactive review CLI + jsonl audit log. Library
only; no CLI wiring at this step.
P5.1 Single remote repo import
`teamai import --from-repo <url>` shallow-clones into
~/.teamai/cache/repos/<provider>/<owner>/<repo>, reuses the existing
generateCodebaseMd scanner, and writes a per-repo summary at
docs/team-codebase/repos/<slug>.md. Three-tier auth (HTTPS+token /
HTTPS-anonymous / SSH); tokens are always redacted in error output.
P5.2 Batch import + domain aggregation
`--from-repo-list <yaml>` drives a whitelist with per-entry
{ url, domain, auth, priority } plus org entries (deferred). Failures
on one repo don't block siblings. Pure-template aggregator emits
docs/team-codebase/domains/domain-<name>.md and a top-level
docs/team-codebase/index.md from the per-repo files. Default output
root moved to docs/team-codebase to avoid colliding with the
existing teamai-cli self-codebase at docs/codebase.md.
P5.3 Incremental sync + domain drift
`--incremental` skips the full clone when the cache is hit and
LAST_SYNC is present, falling back to a fresh shallowClone if fetch
fails. After scan, a fresh recommendDomain pass is compared against
the existing assignment; divergent recommendations (different
domain + confidence > 0.5 + delta > 0.4) land in
domains.history.jsonl as a drift event without auto-reassigning.
AI failures never block the main flow. CI scheduling examples
shipped under examples/ci/ for GitHub Actions and Coding CI.
P5.4 Org bootstrap + iWiki dual output
`--from-org <org> --bootstrap` lists repos via gh api (paged with
/orgs/<o>/repos -> /users/<o>/repos fallback), AI-clusters them,
and walks the user through reviewDomains to produce both
domains.yaml and repo-whitelist.yaml before chaining into
importFromRepoList for the first full sync. TGit listOrgRepos is a
stub (Phase 6).
`--from-iwiki --iwiki-dual` extracts business APIs / external
knowledge / glossary into docs/team-codebase/external-knowledge.md
guarded by HTML comment anchors so future syncs replace bodies in
place. `--require-review` defers section writes to
.teamai/pending-review.jsonl. A small source-conflict helper flags
multi-source updates within a 24h window.
Filesystem layout introduced
============================
docs/team-codebase/
index.md # business-domain map + repo index
domains/domain-*.md # per-domain aggregate
repos/<slug>.md # per-repo detail (from --from-repo)
external-knowledge.md # iwiki-extracted sections
.teamai/
domains.yaml # business-domain dictionary
domains.draft.yaml # AI cluster draft
domains.history.jsonl # decision audit
repo-whitelist.yaml # repo allowlist
source-marks.jsonl # multi-source conflict tracking
pending-review.jsonl # deferred high-risk changes
~/.teamai/cache/repos/ # shallow-clone cache + LAST_SYNC
Surface area
============
CLI flags added to `teamai import`:
--from-repo / --from-repo-list / --from-org / --bootstrap
--depth / --ssh / --domain / --concurrency / --skip-aggregate
--incremental / --max-repos / --exclude-archived
--include-pattern / --exclude-pattern / --skip-import
--iwiki-dual / --require-review
Tests
=====
~95 new unit tests across 14 new test files. Full suite passes
1165/1170 (the one remaining failure in types.test.ts pre-dates this
change). tsc clean (only the pre-existing recall.test.ts error is left).
Out of scope (tracked in Phase 6)
=================================
- TGit listOrgRepos real implementation (currently a stub)
- Cache LRU + 5GB cap + GC command
- Section-level diff with in-place anchor updates
- pending-review CLI to consume the deferred changes
- Domain-drift auto-apply workflow
- Global codebase doc lint
--other=phase5-team-codebase
* feat(agents): multi-CLI subagent sync + security hardening
- introduce YAML intermediate spec for team agents with renderers
for claude / claude-internal / codebuddy / codex / codex-internal / cursor
- add agents path for codex / codex-internal / cursor in toolPaths
- pull renders per target tool format (.md / .toml); push reverses
native files back to YAML, warns and skips on conflicts
- legacy agents/*.md still synced to claude-family for back-compat
Security fixes:
- clone.ts: drop token-in-URL, use http.extraHeader + sanitizeGitUrl
- ai-client.ts: execFileSync with shell:false, timeout, CLI whitelist
- path-safety.ts: assertSafePath + assertSafeResourceName
- import-local.ts: path traversal guard on --dir / output
- push.ts --skill / status.ts --agent: assertSafeResourceName
- env-commands.ts: env list masked by default, add --reveal flag
CSIG fixes:
- parseAgentYaml returns ParseResult instead of throwing
- fileContentEqual catch logs the error instead of swallowing
* feat: Phase 6 — Phase 5 hardening pass
Phase 5 shipped the team-codebase aggregation pipeline; in shipping
it we deliberately deferred a handful of reliability concerns to keep
each step deliverable. Phase 6 closes those gaps -- no new capability
surface, just turning the Phase 5 outputs into something safe to run
in production indefinitely. Six sub-steps, three independent and
three chained, merged here as one commit per project MR rules.
What's in the box
=================
P6.5 Global codebase doc lint (src/codebase-{lint,cmd}.ts)
A deterministic, AI-free cross-file lint over docs/team-codebase
and the .teamai/ controls. 12 categories spanning anchor
integrity, repo/whitelist consistency, sync staleness, frontmatter
completeness, multi-source conflict, etc. `--fix` is intentionally
narrow: only mechanical low-risk actions (orphan-md → archived,
schemaVersion backfill, index counts refresh). High issues that
aren't fixable end up in the skipped list. Exit 1 when any high
remains so CI can gate merges. `--json` for downstream tooling.
P6.0 TGit listOrgRepos real implementation
Replace the Phase 5.4 stub. Uses TGit's GitLab-style OpenAPI:
GET https://git.woa.com/api/v3/groups/<encoded-path>/projects
with token from the existing gfGetOAuthToken() helper. Multi-level
group paths are URL-encoded whole. Pagination loops to maxRepos
(200 default). Field mapping matches the GitHub side; primaryLanguage
is left blank because the list endpoint doesn't return it. Errors
are clean: 404 → "group not found or no access", other HTTP →
"TGit API HTTP <code>: <body>", missing token → explicit hint
about ~/.netrc / TAI_PAT_TOKEN. Token never reaches a log line or
Error message.
P6.1 Cache lifecycle (LRU + size cap + GC command)
The Phase 5 shallow-clone cache only grew. P6.1 adds an explicit
metadata file at ~/.teamai/cache/repos/.cache-index.json; every
successful clone/fetch in importFromRepo now refreshes its row
via touchCacheEntry() (wrapped in try/catch + log.debug so cache
bookkeeping never blocks the import). GC algorithm: stale-evict
> 30d, then if over cap (5GB default, override via
TEAMAI_CACHE_MAX_BYTES or --max-bytes) evict by ascending
last_used until totalBytes ≤ cap*0.8. Eviction order is safe:
fs.remove first, only then splice from index; failures land in
skipped[]. getCacheStatus auto-heals index entries whose physical
directory is gone. CLI: `teamai cache --status | --gc [--dry-run]
[--max-bytes N] [--stale-days N] [--json]`.
P6.2 Section-level diff + in-place anchor updates
The Phase 5 --incremental flag skipped clone but still rewrote
docs/team-codebase/repos/<slug>.md whole, producing churn even
when the source repo had no real change. P6.2 turns those
summaries into anchored sections so unchanged content survives
byte-equal across sync runs. Every `## title` block is wrapped in
<!-- managed-by: import --from-repo, section: <slug>,
source: ..., syncedAt: ... -->
## <title>
<body>
<!-- /managed-by: <slug> -->
Section slugs derive mechanically from the title; duplicate slugs
in one file get -2 / -3 suffixes so split / parse stay aligned.
generateCodebaseMd is intentionally NOT touched -- the AI still
emits one whole markdown blob, and the --workspace path that
maintains the teamai-cli self codebase (docs/codebase.md) is
untouched. Anchors only apply to per-repo team-codebase outputs;
importFromRepo runs the AI output through mergeWithAnchors() per
slug:
- same body hash → kept (old syncedAt + source preserved)
- body changed → rewritten with fresh body + new meta
- present in fresh only → added (appended)
- present in old only → removed (dropped)
The frontmatter rule that actually delivers byte-equal: if all
sections are kept, the prelude is also taken from old, otherwise
from fresh. Without this tie-break the fresh `lastUpdated: <ISO>`
in frontmatter would mtime-bump the file every run.
P6.3 pending-review CLI
Phase 5.4 wrote .teamai/pending-review.jsonl when --require-review
fired but provided no consumer. P6.3 adds the consumer:
teamai review # list (sorted by risk desc)
teamai review <id> # show details
teamai review <id> --apply # apply + drop + audit
teamai review <id> --reject [--reason ...]
teamai review --all-apply [--max-risk medium|low]
Schema upgraded to {id, ts, kind, target, payload, source, risk}
with backward-compat: loadPendingReview() normalises old rows
on read, computing id from sha1(file|section|ts).slice(0,12) and
inferring risk from a small hardcoded set of high-risk sections.
iwiki-dual.ts now writes through appendPendingReview() so new rows
always land in canonical shape. --apply only handles
kind=codebase-section -- it calls patchManagedSection (P6.2),
writes a fresh syncedAt, drops the row, appends an audit event.
Other kinds gracefully degrade to "not auto-applicable". Atomic
write through .tmp+rename so partial writes can't corrupt the
jsonl.
P6.4 Domain-drift auto-apply workflow
P5.3 detected drift and wrote history.jsonl, but gave the user
no way to act on it. P6.4 turns drift into an actionable backlog:
detectDomainDrift now dual-writes -- besides history.jsonl, every
new event lands in pending-review.jsonl as kind=domain-drift,
deduped 24h per url so a re-drifting repo stays one open item
instead of growing. CLI:
teamai domains drift # list
teamai domains drift <repoUrl> --apply
teamai domains drift <repoUrl> --lock
teamai domains drift --apply-all [--threshold 0.8]
Apply does the actual reassignment (splice old → push new,
auto-create the new domain after a TTY confirmation; non-TTY
refuses), updates confidence/signal from the recommendation,
audits via appendHistory(reassign), drops the row, then calls
regenerateAggregate so domain-*.md and index.md catch up. Lock
sets RepoEntry.locked=true and clears stale drift items for the
url. apply-all walks confidence-desc, applies above threshold,
failures don't abort the batch.
CLI surface added
=================
teamai cache --status | --gc
teamai codebase --lint [--fix]
teamai review [id] [--apply | --reject | --all-apply]
teamai domains drift [url] [--apply | --lock | --apply-all]
Tests
=====
~150 new unit tests across 14 new test files. Full suite passes
1344 / 0 failing on this branch (Phase 5's pre-existing
recall.test.ts / types.test.ts failures were fixed in main between
Phase 5 and now and stay green here too). tsc clean.
Out of scope (tracked as Phase 7 in roadmap_jael.md)
====================================================
- Two-level domain hierarchy (e.g. AI/inference, platform/CI)
- Active cross-repo duplicate detection
- codebase.md ↔ search-index/recall integration
- agent retrieval effectiveness metrics
--other=phase6-team-codebase-hardening
* chore: stop tracking local drafts (roadmap, validation, .codebuddy)
These files exist in the working tree to support local iteration on
the team-codebase pipeline (personal roadmap notes, internal phase
acceptance reports, and the .codebuddy plan tree), but they should
not land in the upstream open-source repository. Add them to
.gitignore and untrack them via `git rm --cached` so they:
- stay on disk for local use
- stop showing up in `git status` for daily work
- disappear from the diff against upstream when sending PRs
This is a tracking change only -- no code or test behaviour is
affected.
* docs(readme): trim subagent phase notes and add Phase 5/6 commands
Three adjustments based on mentor feedback:
1. Remove the "Recall via subagent (Phase 1)" subsection from both
README.md and README.zh-CN.md. The phase-numbered design note
was useful during development but reads as roadmap detail on
the public README. The downstream paragraph that explains what
teamai recall actually returns -- the [<type>] tags plus the
four-category index table -- is kept; that one is product
behaviour, not phase trivia.
2. Tighten the public-facing tool list to the openly distributed
editors (Claude Code, Codex, Cursor, CodeBuddy IDE, OpenClaw,
WorkBuddy) and the matching ~/.claude/skills, ~/.codex/skills,
~/.cursor/skills, ~/.codebuddy/skills paths. No code changes:
the underlying tool registry, sync paths, usage tracker, agent
format dispatch, and AI-client probing are all left untouched,
so existing setups keep working as before -- only the public
README is shorter.
3. Add the recent commands that landed in PR #6 / #8 to the table:
teamai import --from-repo / --from-repo-list / --from-org /
--from-iwiki [--iwiki-dual]
teamai cache --status | --gc
teamai codebase --lint [--fix]
teamai review [id] [--apply | --reject | --all-apply]
teamai domains drift [url] [--apply | --lock | --apply-all]
Documentation only; no code or test changes.
* fix(p5-p6): address audit findings — 2 blockers, 1 major, 5 medium
Independent review of the Phase 5 / Phase 6 codebase surfaced eight
issues; this commit fixes all of them. No new dependencies.
Blockers
========
1. iwiki anchor prefix mismatch broke `teamai review --apply`.
iwiki-dual writes `<!-- managed-by: import --from-iwiki, ... -->`
but the parser in section-patcher locked the prefix to
`--from-repo`, so any pending-review item produced via
--iwiki-dual --require-review threw `section not found` on apply.
Fix: relax the parsing regex on both sides (parseSections and
patchManagedSection) to accept `--from-(?:repo|iwiki)`. Writers
stay as-is so the source-of-truth is still recoverable from the
anchor metadata.
2. `--from-org` silently dropped private repos.
`&type=public` was hardcoded into the GitHub list-org-repos URL,
so on enterprise / internal orgs (mostly private) bootstrap
produced a near-empty draft without error. Removed the query
parameter -- relying on the caller's auth visibility (gh CLI or
GITHUB_TOKEN) is the right default and matches GitHub's `type=all`.
Major
=====
3. tryEndpointPrefix returned success when the first page was empty.
Combined with the bug above, an internal org with all-private
repos returned [] from `/orgs/<x>` and never tried `/users/<x>`.
Fix: when items.length === 0 && page === 1, return false so the
outer code falls back to the user endpoint. Applied to both the
gh CLI branch and the fetch branch.
Medium
======
4. ReDoS hardening on section-patcher anchor regexes.
`[^>]*?` could be coaxed into exponential backtracking by hostile
input. Replaced with `[^>\n]{0,256}?` -- bounded character class
plus length cap. Applied to all four open/close anchor regexes
(parseSections + patchManagedSection, both directions).
5. 10 MB hard cap on YAML / JSON config reads.
loadDomains, loadCacheIndex, loadRepoList, and loadPendingReview
now stat() before readFile and reject anything over 10 MB. Stops
a malformed or hostile config file from blowing up memory.
6. Final path-safety check before per-repo writeFile.
importFromRepo now calls assertSafePath() (an existing helper from
PR #7) on the resolved repos/<slug>.md path. Defence-in-depth on
top of the existing slug sanitisation; refuses to write outside
the configured reposDir even if a future code path generates a
weird slug.
7. SSRF guard + 50 MB response cap on outbound HTTP.
gh-org and gf-org's fetch path now sets `redirect: 'manual'` and
throws on any 3xx, and reads the body as a stream that cancels
the reader and throws once total bytes exceed 50 MB. The gh CLI
branch is unaffected -- gh handles redirects itself.
8. Backup before mergeWithAnchors fallback.
When the existing repo file has corrupt / unclosed anchors,
parseSections used to silently throw and importFromRepo fell
back to a full-rewrite, losing every prior syncedAt timestamp.
It now writes the old file to <repoMdPath>.bak (single overwrite,
no accumulation) before doing the fallback wrap, so the prior
state is recoverable.
Tests
=====
- iwiki-review-apply.test.ts (new): end-to-end -- iwiki-dual writes
to pending-review.jsonl with --require-review, then `teamai review
<id> --apply` is asserted to actually mutate external-knowledge.md
(string contains the new body, anchor still says `--from-iwiki`).
This was the regression that the parsing-regex fix unblocks.
- gh-org.test.ts (new): three cases -- private repos visible without
type=public; first-page empty on /orgs/ falls back to /users/;
/orgs/ 404 also falls back to /users/.
- section-patcher.test.ts: added cases for splitToSections /
parseSections / patchManagedSection on iwiki-flavoured anchors.
- domains-store / cache-index / repo-list / review-store: each
gained a test that writes an actual 11 MB file to a tmpdir and
asserts the loader rejects it (not mocked).
- import-repo-merge.test.ts: corrupted-anchor case asserts the
.bak file appears with the original content.
96 test files / 1356 tests pass / 0 failures (12 added by this
commit). tsc has only the pre-existing recall.test.ts error (carried
over from main, unrelated). Line length still ≤ 120 across all
touched files.
--other=fix-p5-p6-audit
* fix(test): add missing `type` field to recall.test.ts SearchIndexEntry mock
upstream CI's `Type check` step (npx tsc --noEmit) blocked PR #28 with:
src/__tests__/recall.test.ts(62,7): error TS2741: Property 'type'
is missing in type '{ filename, title, author, date, tags,
tokens, votes }' but required in type 'SearchIndexEntry'.
SearchIndexEntry was extended in Phase 1 with a required `type:
KnowledgeType` field for the multi-bucket index, but the mock
factory in the recall vote test didn't follow. Local vitest doesn't
type-check the source so the bug never surfaced; upstream CI does
run tsc strictly and the typecheck step gates everything else
(unit tests, build, e2e), which is why PR #28 failed at the very
first step.
The test only exercises recallVote's counter path -- the `type`
field is never read -- so 'learnings' is just a representative
default; behaviour is unchanged.
Verified locally:
- npx tsc --noEmit → 0 errors (was 1)
- npx vitest run recall → 9/9 passing (unchanged)
- npx vitest run → 1360/1360 passing (unchanged)
- npm run build → success
* fix(test): resolve cross-platform path assertion failures in review-cmd.test.ts
--story=fix-github-actions-test-failures
Replace absolute path assertions with expect.stringContaining() to handle different tmp directory paths on macOS (/private/var) vs Linux (/var)
---------
Co-authored-by: jaelgeng <jaelgeng@tencent.com>
|
||
|
|
5f1c4e81e1 | 0.16.6 | ||
|
|
5015eb95b2 |
feat: wiki toggle via env var + wiki path fixes + README i18n
- feat: add wiki feature toggle via environment variable (closes #21) - fix: align wiki skill path resolution with teamai push scope logic - fix(push): exclude .git from copyDir to prevent submodule gitlink - refactor: wiki 存储路径从各工具目录统一到 ~/.teamai/wiki/ - fix: filter rules by role knowledge namespaces during pull - docs: split README into English (default) and Chinese versions |
||
|
|
585ce9715b | 0.16.6 | ||
|
|
1a18e72c74 |
fix: sync with GitHub - wiki skill path + package.json format (merge request !188)
Squash merge branch 'fix/sync-wiki-path-from-github' into 'master' Sync from GitHub main (PR #15 merged): 1. skills/teamai-wiki/SKILL.md - wiki path scope-aware 2. package.json - bin path and repo url format |
||
|
|
cbd54795be | 0.16.5 | ||
|
|
883d0aea0a | 0.16.4 | ||
|
|
a7298b5823 | 0.16.3 | ||
|
|
1b5a71e8da |
docs: update project tagline to The team harness for AI agents (merge request !180)
Squash merge branch 'worktree-update-tagline' into 'master' ## Summary - 更新项目 tagline 为 "The team harness for AI agents" - 副标题明确两大核心能力:(1) 跨 AI Agent 同步 skills/rules/docs;(2) 构建团队共享知识库 - 涉及文件:README.md, README.en.md, package.json, src/index.ts ## Test plan - [x] grep team harness 命中 4 个文件 - [x] 旧 tagline 不再出现 - [x] npx tsc --noEmit 通过 |
||
|
|
0bf2a94e18 |
chore: normalize package.json bin path and repository url
Auto-applied by `npm pkg fix` during 0.16.2 first publish to public npm: - bin.teamai: "./dist/index.js" -> "dist/index.js" - repository.url: "https://..." -> "git+https://..." Co-authored-by: Cursor <cursoragent@cursor.com> |