mirror of
https://github.com/Tencent/teamai-cli.git
synced 2026-10-02 03:14:40 +08:00
* 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 e151d43, 1ca43ac,
8bb0548 and 2ddb546 before they shipped; it carries a case proving it catches
`teamai extract graph`.
Paths. Everything the skills told an agent to read or execute assumed the skill
sat in the agent's own directory: `python3 scripts/scan_repo.py` from a cwd that
is the target repo, references cited by bare filename in two different
conventions, methodology paths handed to sub-agents inside input packets. All of
them now go through {SKILL_DIR}, which `skill get` resolves. The README template
nobody referenced is wired into the step that writes the knowledge-base README.
Frontmatter. None of the three skills declared allowed-tools, so the first
command of every flow hit a permission prompt. The wiki skill kept its trigger
words and prerequisites inside the description text; both move into the body.
Splits. `core` keeps what a daily user needs and `setup` takes day 0 and the
repo lifecycle, so the common path no longer carries ~500 lines of repo
creation. The wiki skill's phase procedures move into references/phases/, taking
its SKILL.md from 38.7 KB — larger than agent-browser's entire core — to 17 KB
with an index that says when to load each phase.
One contradiction is resolved in the author's text: the share skill mandated
that every generated document be written in Chinese, against global rule 1
("reply in the user's language") and this repo's own English rule. It now
follows rule 1.
Refs #678
* feat(pull): prune legacy builtin skill directories, gate recall at run time
Upgrading the CLI used to leave the pre-stub trees in place: cleanup skips
builtin names, and nothing else knew about them, so `team-wiki-codebase` and
`teamai-share-learnings` would sit in every agent directory on the machine
forever. Deployment now removes them first, in both the configured skills path
and Codex's shared `.agents/skills`. Unconditional, because those trees were
overwritten on every pull, so no local edit ever survived in them.
Recall moves from deploy time to run time. Before, `skipRecall` decided whether
the share skill reached the agent at all; with one stub routing to everything,
there is no directory to withhold, so `teamai skill get share` checks instead
and says what to enable. `--all` is exempt: an inventory dump is not an attempt
to run the workflow. With no team config to consult the gate fails open — a
fresh machine reading the docs gets the content rather than a refusal it cannot
act on.
deployBuiltinSkills drops its `skipRecall` option rather than keeping one that
no longer decides anything, and recall-toggle stops deleting a skill directory
it no longer owns.
Refs #678
* docs: align the nudge and the guides with CLI-served skills
`/teamai-share-learnings` was never a slash command of its own — it existed
because the directory was installed. The Stop-hook nudge now names `/teamai` and
carries `teamai skill get share` literally, so an agent can act on it without
having to infer the intent from the conversation. The five READMEs and both
usage guides follow.
Both guides gain the `skill get` / `skill path` commands and a short section on
why built-in skills are served rather than copied. `docs/designs/skill-serving.md`
records the contracts that are easy to break later: byte-for-byte output,
{SKILL_DIR} substitution, stdout/stderr discipline, recursive `--full`, the
run-time recall gate, the three drift guards, and when to retire
LEGACY_BUILTIN_SKILL_NAMES and the long-name aliases.
AGENTS.md and CLAUDE.md gain the rule that keeps this from rotting: skill-data
is treated like documentation, a behaviour change updates the affected skill,
commands.md is regenerated rather than edited, and new workflows go under
skill-data instead of into the stub.
Refs #678
* fix(skills): apply standards review findings
`teamai init` printed "Built-in skills (e.g. team-wiki-codebase) are ready to
use in your IDE now" seconds after deployment deleted that very directory. The
message now names the teamai skill and how it loads its workflows. Two comments
carrying the same stale name follow.
The five READMEs said different things: only the English one named the share
workflow and its command. All five now do.
`collectSupplementaryFiles` hand-rolled a recursive walk that
`listFilesRecursive` already does, including the ignore list that skips `.pyc`
and `__pycache__` next to the wiki's Python scripts. It calls the helper
instead. `listServableSkills` drops its fallback to `skills/`: a package without
`skill-data/` is broken, and serving the stub as if it were the content hides
that from the one error message built to report it.
Tests drop six non-null assertions for a helper that throws, per the repo's rule
against moving a compile-time error to run time.
AGENTS.md and CLAUDE.md record the exemption the branch created: skill content
printed by `skill get` keeps the language its author wrote it in, while the
command's own prompts, errors and listings stay English.
Refs #678
* fix(skills): apply spec review findings
Upgrading left the old references in place. Releases before the stub deployed
`skills/teamai/` with six reference files beside SKILL.md, and `teamai` is not a
legacy name to prune, so copying one file over that directory kept ~39 KB of
pre-stub instructions next to the new stub for good. Deployment now clears
everything the deployed unit does not contain before writing it, and a test
seeds the old layout to prove it.
Pruning reached neither reporting-only teams nor the Codex shared directory in
any test. The prune now runs before the reporting-only return, so a team that
switched to reporting-only still loses the stale trees, and the Codex
`.agents/skills` path is covered by a test. Excluded agents stay untouched, as
the enabledAgents whitelist documents.
The served content still routed to skills that no longer exist: five mentions of
`teamai-share-learnings` and one `/team-wiki-codebase --update`, which is the
rule this branch itself added being broken on arrival. One second-hop path
inside a sub-agent input packet was still relative.
The share skill's document template, frontmatter table and tag taxonomy move to
`references/doc-template.md`, taking the always-read body from 3 701 to 2 471
bytes. Two tests now assert what nothing guarded: every served skill's
frontmatter name matches its directory and declares allowed-tools.
A new e2e file runs the built CLI the way an agent does: every listed skill is
servable and byte-identical bar the resolved placeholder, the wiki scripts run
from the directory `skill path` prints, an unknown name exits 1 with empty
stdout, a hallucinated flag warns and still serves, and `--full` appends the
nested references in sorted order.
Refs #678
* fix(skills): prune only directories the CLI owned, gate every content path on recall
- LEGACY_BUILTIN_SKILL_NAMES drops teamai-workflow and teamai-import: they were
reserved in the old guard set but never packaged, so a directory by either
name is the user's own skill. Test: user-created skills with those names
survive pull.
- The recall gate now covers skill get --all (blocked skill skipped, named on
stderr), skill path (refused) and skill list --json (blockedByRecall, path
null). skill list reads the flag from the catalog instead of re-checking.
- skill get [names...]: the positional is optional so --all is reachable from
the real CLI; Commander used to fail with 'missing required argument'.
Covered by the skill-serving e2e.
- commands-reference renders Commander's variadic marker (<names...>); the
snapshot is regenerated.
* test(skills): drive the recall gate through the real CLI, guard the stub description budget
- skill-serving e2e: a HOME with a team whose recall is off; skill get share,
--all, skill path share and skill list --json each withhold share, and all
serve it after recall enable. The earlier HOME has no team config and fails
open, so the gate was never exercised through dist/index.js.
- skill-content test: the stub description stays within 1024 characters.
- skill-commands-exist also scans the deployed stub.
- Docs and PR lead with content versioned with the CLI; the size numbers are
measured (stub description 0.8 KB, body 1.3 KB; --full 32/36/115 KB).
- Content audit against origin/main: every file has a counterpart. Fixes:
{SKILL_DIR} defined in core/setup/share where the references are listed, the
wiki overview draws the served layout, team-wiki-codebase kept as a trigger
word in the stub description.
* fix(skills): gate skill show on recall, prune Codex's shared dir only from Codex
Review follow-up on #699.
- `skill show <served skill>` refuses a recall-blocked skill with the same
message and exit code as `skill get` / `skill path`; it printed the
directory those two withhold.
- A skill resolved from skill-data/ is classified `[builtin]` directly.
BUILTIN_SKILL_NAMES only knows the deployed stub, so `skill show core`
reported `[local-only]` beside a package path.
- pruneLegacyBuiltinSkills reaches `.agents/skills` only on Codex's own
pass. Another enabled tool's pass deleted Codex's legacy copies while
Codex was excluded, against the enabledAgents guarantee.
- The share skill and its references are written in English; the generated
document still follows the session's language. The AGENTS.md exception
for Chinese skill-data output is dropped.
* fix(skills): one resolver for served skills, legacy names kept out of push, wiki in English
Review follow-up on #699.
- resolveServableSkill is the only way to obtain a PackagedSkill outside
skill-content.ts; it returns `blocked` instead of the skill, so `get`,
`path`, `list` and `show` inherit the recall gate by construction.
- push never offers `team-wiki-codebase` / `teamai-share-learnings` as new
user skills: between the upgrade and the first pull they are still on
disk (isCliOwnedSkillName).
- `recall disable` removes the legacy `teamai-share-learnings` directory
again (LEGACY_RECALL_SKILL_NAMES), skipping excluded agents.
- `skill list` prints the packaged catalog before `teamai init`, with a
hint for the team half, instead of failing on the team listing.
- skill-data/wiki (SKILL.md, 14 references, 2 scripts) translated to
English. Generated document names follow one glossary; validate_kb.py
still recognises headings of knowledge bases built by the previous
release, matched by code point so the source stays ASCII.
* fix(skills): prune only files the CLI packaged, let local skills win by name
Review follow-up on #699.
- PACKAGED_SKILL_FILES lists every file a release ever wrote under skills/,
as the union of `git ls-tree -r <tag> -- skills/` over all 91 tags. The
prune removes those paths and the directories they leave empty; a file a
member added is kept, its directory with it, and pull says which and why.
The stub directory loses its six known references by name instead of
"everything that is not SKILL.md". Python bytecode of a script we shipped
counts as ours, so a __pycache__ does not strand the tree.
- locateSkill searches the team repo, then installed agents, then the
package. A directory a member created under `codebase`, `default`,
`learning` or `share` is the skill they asked about, and the recall gate
does not apply to it.
- A guard test fails when a file ships under skills/ without being recorded
in the manifest, which a later migration would otherwise leave behind.
* fix(skills): close the last recall bypass, uninstall Codex's shared stub
Review follow-up on #699.
- `skill path` takes a name, always. The argument-less form printed the
`skill-data/` root, and `<root>/share/SKILL.md` is readable from there —
the content the gate withholds one command over.
- uninstall discovers skills in Codex's shared `.agents/skills` root, where
resolveSkillDestination puts the stub whenever the skill already lives
there. Without it, uninstall reported success and left it behind. Codex
only, as the legacy prune already does.
- core/SKILL.md said team sharing is enabled by default; getRecallSharing
defaults it to false. It now says recall is off by default and names
`teamai recall enable`.
* fix(skills): uninstall by the same ownership rule as pull, quote {SKILL_DIR}
Review follow-up on #699.
- uninstall removed a CLI-owned skill directory whole, undoing one command
over the guarantee pull makes. It now removes the PACKAGED_SKILL_FILES
paths through the same removeOwnedFiles, keeps a directory holding a file
the member added, says which one, and tells the confirmation prompt so it
no longer promises a directory it will keep. A team-repo skill is synced
whole and still goes whole.
- Served shell commands quote the placeholder: `python3 "{SKILL_DIR}/..."`.
Unquoted, an install path with a space ("Program Files", "Application
Support", a Windows path through Bash) splits into two arguments and the
documented invocation fails. A test fails on an unquoted occurrence after
any command word, in SKILL.md or any reference.
- wiki/references/overview.md said the methodology, scripts and agent specs
are deployed into agent directories. They are not: only the stub is, and
the rest is served from the installed CLI.
* fix(skills): deploy the stub in reporting-only mode, drop the stale list alias
Review follow-up on #699.
- Reporting-only HTTP pull pruned the legacy trees and deployed nothing, so
a member on an HTTP team came out of the upgrade with no built-in entry
point at all. The skip predates CLI-served content: it existed because the
only deployable unit then needed a team repo. The stub does not — its
workflows are printed by the installed binary, and `skill get wiki` is a
local knowledge-base generator that never touches a repo. The stub now
deploys in every mode, and `reportingOnly` goes with the branch it gated:
nothing else read it.
- `teamai skill list` called itself an alias for `teamai list skills
--source all`. It has not been one since it started printing the CLI-served
catalog underneath. Both descriptions, the generated command reference and
both usage guides now say what it does.
Refs #678
* fix(skills): carry the TGit provider guide into the served setup skill
#724 landed `skills/teamai/references/provider-tgit.md` and repointed
setup-admin.md and join-member.md at it. Rebasing onto that left the new
file in a tree this branch no longer deploys, and the pointers in bare
`provider-tgit.md` form the served skills do not use.
- move it to `skill-data/setup/references/`, beside the two files that
cite it, so `teamai skill get setup --full` serves it
- rewrite every pointer to it as `{SKILL_DIR}/references/provider-tgit.md`
- list it in the setup skill's reference table
- add `references/provider-tgit.md` to PACKAGED_SKILL_FILES, so the prune
removes it from members who pulled a release that shipped it
* fix(skills): make the blocked catalog entry unrepresentable, drop unsafe casts
Review findings from the standards axis, plus the doc half of the prune
count.
- `SkillCatalogEntry` allowed `{blockedByRecall: true, path: '/…'}`, an
invariant `skillCatalog` then upheld by hand. Split it on
`blockedByRecall`, so the withheld directory is a type error rather than
a review catch. Both variants keep the `path` key, so the
`skill list --json` shape is unchanged.
- `command.commands as Command[]` stripped commander's `readonly` in three
places. `for…of` and `.find` need no cast.
- `docs/designs/skill-serving.md` still said the prune removes six
`teamai/references/*.md`; provider-tgit.md makes it seven.
* fix(skills): keep publishing a skill reachable when recall is off
Publishing a skill is `teamai push --skill`, which never consulted recall
(`src/push.ts` names it nowhere). On main the flow shipped in the teamai
skill, ungated. Moving `contribute-member.md` under `share` put it behind
the recall gate, so with recall off — a new team's default — the core
routing table sent the agent to `teamai skill get share`, which exits 1
and tells it to enable recall. Wrong advice for a flow recall does not
touch, and no other path to the instructions.
Move the file to `core`, the skill that already owns `push`, and split the
routing row so publishing and session learnings stop sharing one
destination. The gate itself is right and stays: learnings do need recall.
`share/SKILL.md` already called this "a different flow"; now it points at
`teamai skill get core --full` instead of at its own references.
PACKAGED_SKILL_FILES is unchanged: the legacy path a pre-stub release
wrote is still `teamai/references/contribute-member.md`.
* fix(skills): back up what the prune removes, so no edit is a one-way door
Review finding: `removeOwnedFiles` proves ownership by pathname and
deletes without reading the file, so a member's edit goes with it.
For a path the current package still ships that changes nothing: the old
deployment overwrote it with `overwrite: true` on the same three triggers,
so the edit died either way, at the same moment. The case the objection
gets right is a path a retired release shipped and the package no longer
does — the overwrite never reached it, so the edit did survive, and the
prune is the first thing to remove it.
Copy every pruned file to `~/.teamai/removed-skills/<date>/<tool>/<skill>/`
before removing it. Outside every agent directory, so nothing reads it back
as a skill.
Verifying contents against a hash of each released version was the other
way out, and it is worse: anything not byte-identical is then kept, so one
CRLF checkout on Windows — a platform this project supports — leaves the
whole 176 KB in place and reports success. Backing up gives the same
guarantee without betting the migration on byte equality.
Uninstall keeps deleting outright: there the member asked for the files to
go.
* fix(skills): let no backup failure authorise a delete, give each root its own
Two holes in the backup the previous commit added, both reported in review.
The copy's failure was swallowed at debug level and the delete went ahead
regardless, so a full disk or a read-only home turned the migration back
into the data loss the backup exists to prevent — and the log still named
a backup directory that held nothing. A file whose copy fails is now kept,
counted, and named at warn level; `removeOwnedFiles` returns what happened
instead of a bare boolean, and only a run that copied something names the
directory.
The backup path was `<date>/<tool>/<skill>` with `overwrite: true`, so the
second copy of a name silently replaced the first. Codex prunes the same
skill from `.codex/skills` and the shared `.agents/skills`, and two pulls
share a date. The path now carries a per-run id and the skill root, and the
copy refuses to overwrite rather than clobbering a copy it cannot replace.
Tests cover both: a file where the backup tree must start makes every copy
fail, and the two Codex roots land in separate directories. Each fails
against the previous commit.
* fix(skills): stop at a symlinked root, archive only what is retired
Three review findings, all in the prune.
A symlinked skill directory was walked through. `readdir` follows the link,
every path under it matches a packaged name, and the delete lands in
someone else's checkout. Ownership now stops at the link: the root is
lstat'd, a symlink is refused, and link and target are left alone.
The stub directory was pruned against the full historical file list, which
includes the SKILL.md written one line later. Deployment runs on every
session start, unchanged revision included, so that archived an identical
copy per session forever. Only paths this release no longer ships are
archived now.
Backups were written under the tool's base directory, which under project
scope is the repo root, so they landed in the working tree outside the
generated .teamai/.gitignore. They go to the machine's home.
Also: `skill show <packaged>` resolved the team before the package, so it
failed on a machine that never ran `teamai init` for content that needs no
team. Packaged names resolve first and print without the team-dependent
fields.
* fix(skills): stop at the first link above a skill dir, report a half prune
Findings from a self-review run before pushing, plus the two from the last
review round.
The symlink guard was one level too low. It lstat'd the skill directory, so
the common shape — `~/.claude/skills` itself linked at a dotfiles checkout —
walked straight through: every directory under the link is real. The guard
now walks each component below the tool's base directory and stops at the
first link, which covers the prune and the stub write with one check.
Components at or above the base are not checked: a home directory under a
link is ordinary, and refusing there would disable deployment on those
machines.
The symlink branch borrowed the foreign-files message, so a member was told
"delete the rest yourself" about a directory nothing had touched. Following
that destroys what the guard just protected. It has its own sentence now, in
pull and in uninstall.
`remove()` was not fail-closed the way the backup is: a read-only parent
left the tree half-pruned under a debug line, and a `walkFiles` that threw
returned success. Both are recorded in `notRemoved` and reported.
The backup path gained the base directory: `inheritUserScope` deploys the
user base and then the project base in one process, same tool, same root,
same skill name, and `errorOnExist` turned that collision into files the
second pass could neither archive nor prune.
Docs corrected against the code: the archive path, the tag count (98, not
91, and `teamai-wiki` is excluded), the version line, and the size table.
* fix(skills): route the nudge and skill publishing where they land, classify legacy names as ours
The Stop-hook hint said "run /teamai", but bare /teamai prints the menu and
stops, so following the primary suggestion never reached the share workflow.
It now names an invocation the core skill routes to share, with the
`teamai skill get share` fallback kept. The four docs that quote the hint follow.
The setup skill sent "publish one skill" to `teamai skill get share`, which
handles session learnings and is refused when recall is off (the default);
reusable-skill publishing lives in core's contribute-member reference and needs
no recall. The routing row and the two references that repeated it now point
there.
classifySkill checked BUILTIN_SKILL_NAMES alone, so until the first pull pruned
them, team-wiki-codebase and teamai-share-learnings showed as [local-only]. It
now uses isCliOwnedSkillName, the rule push and uninstall already apply.
* fix(skills): pre-push review — gate the nudge on recall, keep bytecode out of the tarball, report a failed uninstall delete
A review of the whole branch against #678, #730 and the design doc, run before
pushing. What it found and what changed:
- The Stop-hook share reminder was gated on the hint switch alone; recall is off
by default and `teamai skill get share` refuses then, so the reminder pointed
at a command that said no. It is withheld while recall is off, the same gate
the workflow has; the served text about when the prompt appears now matches.
- `npm pack` swept `skill-data/wiki/scripts/__pycache__` into the tarball once
the e2e suite had run the scripts. Excluded in package.json "files", asserted
absent in the tarball test, and the e2e run sets PYTHONDONTWRITEBYTECODE.
- The `share` description still offered to publish reusable skills, the flow its
own body sends to `core`; the sentence is gone.
- `skill show <unknown>` before `teamai init` threw the init error as a stack
trace; it prints the not-found line and exits 1.
- The stub pre-approved every `teamai` command from the always-loaded unit;
narrowed to `Bash(teamai skill:*)`, which is all it asks for (#678).
- Six routing lines loaded `core --full` to reach one reference; they name the
file under `$(teamai skill path core)/references/` instead.
- `uninstall` reported a failed delete as "holds files TeamAI did not put there;
the packaged files were removed", both false and the error unprinted. It names
the file and the error; a test makes the stub directory read-only.
- The stub directory archived under `<tool>/.claude-skills-teamai/teamai/` while
the legacy trees used `<tool>/.claude-skills/<skill>/`; one layout now.
- CHANGELOG entry; dead `isRecallEnabled` import; wiki heading still naming
`team-wiki-codebase`; JSDoc on the wrong declaration; stale byte counts; the
usage guides gain the recall refusal and the archive location; the design doc
records the `--json` deviation, the legacy-name classification rule, the
uninstall symlink scope and the fail-open wording.
* fix(skills): English-only served content, one link guard for every caller, withhold share from read-only sources
The reviewer flagged Chinese in skills/ and skill-data/ a third time. Both
reach the agent as CLI output, so the stub's trigger keywords, the paired
sample invocations and the Chinese name for TGit go; the agent translates for the user. A test
fails on CJK anywhere under either root.
Pre-push review of the whole branch, and what changed:
- uninstall walked through a linked ~/.claude/skills and deleted the
packaged files inside the member's dotfiles checkout; pull refused the same
layout. removeOwnedFiles now owns the guard, so pull, deploy and uninstall
apply one check: the skills root and the skill directory. A linked
~/.claude (stow, chezmoi) is no longer refused, since every other resource
writes through it and refusing left those machines on the pre-stub trees.
- share was served to read-only HTTP teams, where its last step
(teamai contribute) always fails; reportingOnly used to skip it. The
serving gate carries a reason (recall | read-only) with its own message,
and `skill list --json` reports it as `blockedBy`.
- The bytecode rule claimed any file under any __pycache__; it now claims
only the .pyc of a shipped script.
- recall disable pruned the shared .agents/skills root for an uninstalled
Codex; it has deployment's install gate now.
- The source-team guard lost the legacy names when BUILTIN_SKILL_NAMES
narrowed, so a source removal could delete a legacy tree wholesale.
- Routing: the admin wrap-up and the stub still sent "share what I learned"
to share without saying it needs recall, and the stub filed "share this
with my team" (the publish-a-skill phrase) under share. recall enable is
described as the per-machine override it is, next to the team key.
- {SKILL_DIR} definitions now say how a reference file opened on its own
spells the directory, since serving resolves the definition too.
- skill show: packaged resolve only when init fails, aligned label, a
served skill is "served by the CLI, not installed".
- Docs: uninstall removes the archive with ~/.teamai; zh said the whole
directory is kept; the product overview lacked the recall gate; the design
doc's release, tag and byte figures were stale; CHANGELOG notes the
language change of generated documents.
* fix(skills): check every path component below the base for a link, in uninstall too
The previous commit narrowed the guard to the skills root and the skill
directory, so a link at ~/.config or ~/.config/opencode was walked through:
the prune could delete, and deploy write, inside a dotfiles checkout. The
full walk from the tool's base directory is back, and removeOwnedFiles now
requires the base, so uninstall applies it too; each skill directory in the
uninstall plan carries the base its skills root hangs off.
A member whose whole ~/.claude is a link keeps the pre-stub trees and gets
the warning naming the path, as before the previous commit. Deleting through
a link is the one thing the prune must never do.
* fix(skills): withhold the share hint on read-only sources, route legacy names through the gate
contributeHintAllowed checked recall only. The dispatcher already drops this
gitOnly handler for HTTP teams, but the gate now says so itself, so the
reminder never points at a `share` that refuses as read-only wherever it runs.
`skill show teamai-share-learnings` searched the agent directories before
the package, so a legacy tree a pull had not pruned yet was shown with its
path while the gate refused `share`. A legacy built-in name now skips the
agent search and goes to the packaged skill and its gate; ordinary names and
aliases such as `share` keep a member's own directory first.
* fix(skills): deploy and prune built-ins where the tool keeps its skills
deployBuiltinSkills joined baseDir with the configured skills path, while
team-skill sync resolves the directory through skillsDirForTool: OpenClaw's
workspace, and HERMES_HOME for Hermes. Those agents got the stub in a
directory they never read and had their legacy trees pruned from the wrong
place. Deploy, the legacy prune, recall disable and uninstall now resolve
the same directory; the link guard starts at the tool's base directory when
the skills directory sits under it, else at that directory's parent.
* fix(skills): check an external skills root for a link, keep config-load logs off stdout, drop inert allowed-tools
- A skills directory outside the tool's base (HERMES_HOME, an OpenClaw
workspace) had the guard start at the root itself, so a linked root was
never checked. It starts one level above now, and a linked HERMES_HOME is
refused like a linked ~/.claude.
- The share gate loads the config, which can migrate it and report that with
log.info on stdout: an upgrading machine got that line in `skill get`
output and in `skill list --json`. Config loading reports on stderr for
that call; setStderrOnly returns the previous mode so it can be restored.
- `allowed-tools` in the served skills was printed as command output and
never processed as skill metadata, so it granted nothing. Removed, and the
test now fails if one comes back. Only the stub's line pre-approves.
* fix(skills): walk the link guard from the scope root, so a linked COPILOT_HOME is refused
skillsGuardBase started at the tool's base directory, which for Copilot in
user scope is COPILOT_HOME, so the walk never checked whether COPILOT_HOME
itself was a link, and pull and uninstall wrote and pruned through it. The
guard now starts at the scope root (home, or the project root), where a link
at or above is ordinary, in deploy, the legacy prune and uninstall alike; a
root configured outside it still has the walk start just above that root.
* fix(skills): keep generated documents in Simplified Chinese, fail on a broken config, quote skill paths
- share and wiki had moved generated learnings and knowledge-base documents
from always Chinese to the session language. Serving the instructions from
the CLI does not need that, so both say "Simplified Chinese" again, in an
English instruction; the CHANGELOG entry follows.
- skill show and skill list treated every autoDetectInit failure as "not
initialized" and pointed at `teamai init`. requireInit now throws a tagged
NotInitializedError; only that falls back to the packaged catalog, and a
malformed or unreadable config propagates.
- `$(teamai skill path …)/…` is word-split in a shell command like an
unquoted {SKILL_DIR}; all ten occurrences are double-quoted and the quoting
test covers the form.
* fix(config): raise NotInitializedError only when the config file is missing
loadLocalConfig returns null both for a missing file and for one that fails
to parse, validate or migrate (it logs the reason). requireInit turned every
null into NotInitializedError, so skill show and skill list still fell back
to the packaged catalog and a `teamai init` hint on a broken config. Only an
absent file is NotInitializedError now; an existing one that could not be
used is an error naming its path, in requireInit and the user branch of
requireInitForScope. Covered through the real loader and the built binary.
* fix(skills): prove ownership by content, not path alone; retire the second Codex copy
- The legacy prune and uninstall removed any file at a path a release had
packaged, so a member's edit, a skill of their own under an old name, or a
root TeamAI never managed (toolPaths or HERMES_HOME moved) lost its files.
A file is ours now only at a packaged path and with content a release
shipped there: PACKAGED_SKILL_DIGESTS records the sha256 of every blob over
all 99 tags through v0.25.0 and main before the stub, 37 versions across 21
paths. A skill-root SKILL.md is compared by its body, since releases before
0.17 shipped no frontmatter and the deploy of the day repaired it on disk.
The current stub is ours by the packaged copy. Anything else stays.
- Codex reads .codex/skills and the shared .agents/skills, and the stub goes
to the shared one when a copy lives there; the copy an earlier release left
in the other root kept its old SKILL.md and references. It is retired by
the same ownership rule, archived first, and named when kept.
- Tests mock the digest table with a stand-in for shipped content, and a test
keeps the stand-in on the same paths as the real table.
* chore(skills): carry main's skill edits into the served copies after the rebase
#713 and #736 edited skills/teamai/references/*.md, which this branch moved to
skill-data/setup/references/. Two hunks did not follow the move:
- join-member.md: TGIT_TOKEN is REST-API-only and cannot clone (#713).
- setup-admin.md: the /teamai share entry publishes a reusable skill; a
session's learnings are automatic (#736), in English as the served text is.
#739's partial config mock is restored in skip-uninstalled-tools.test.ts.
* fix(skills): deploy before pruning, block share on an unloadable config, drop hidden commands from the reference
- Legacy trees were pruned before the stub was written, so a refused or
failed stub (a link, a read-only directory) left the agent with nothing
to discover. They go only once the stub deployed for that agent.
- The share gate failed open on any config error. Only a machine with no
config (NotInitializedError) is served; a config that exists but cannot be
loaded blocks with its own reason, `blockedBy: "config"`.
- The KB template told agents to run `code-to-knowledge --update`, which
does not exist; it names `teamai codebase --extract … --incremental`.
- The generated command reference listed hidden hook plumbing (`track`,
`contribute-check`, `todowrite-hint`, …). It renders what `--help` lists.
- removeEmptyDirs swallowed every rmdir error, so a directory that stayed
could be reported removed. Only "still holds something" is expected; any
other failure is reported.
* fix(skills): whole-file ownership, stub before its references, no side effects before the link guard
Review of 327f9cd:
- SKILL.md was compared by its body, so a member who changed only its
frontmatter lost the file. Every release from 0.16.1 (the first whose
deploy repaired frontmatter) shipped complete frontmatter, so what is on
disk is what was shipped: digests are whole files now (42 versions over
100 tags and main). A link is never ours; bytecode is ours only beside a
script proven ours by content, decided before anything is removed.
- The stub dir's retired references were pruned before SKILL.md was copied;
a failed copy left the old skill pointing at files that were gone. The
stub is written first.
- The Codex destination was resolved with the reconciliation that deletes a
duplicate, before the link guard ran. It is resolved side-effect free; the
other copy is handled under the guard by retireOtherCodexCopy, whose
report now names a failed backup or delete as such.
- A broken project config was skipped by detection, so the share gate
answered with the user config. findUnreadableProjectConfig reports it via
an optional sink on detection (no caller changes), and the gate blocks.
The Stop-hook reminder is withheld on an unloadable config too.
- init announced the stub as ready when nothing was deployed; hook-dispatch
is hidden (hook plumbing), and the reference says it lists public commands;
the design doc no longer says teamai-workflow/teamai-import are removed.
* fix(config): report a broken higher-priority project config even when a fallback loads
findUnreadableProjectConfig dropped a recorded error whenever detection
went on to find a later candidate: a broken partition config followed by a
valid legacy .teamai/ config returned null, and the share gate answered with
the fallback's team. It now reports the first unreadable file regardless.
An existing config file that is empty or cannot be read is reported to the
sink too, instead of returning without a word.
96 lines
2.2 KiB
JSON
96 lines
2.2 KiB
JSON
{
|
|
"name": "teamai-cli",
|
|
"version": "0.22.0",
|
|
"description": "TeamAI — Make Every Team AI Native (skill sync + shared knowledge base, powered by Git)",
|
|
"type": "module",
|
|
"bin": {
|
|
"teamai": "dist/index.js"
|
|
},
|
|
"files": [
|
|
"dist/**/*.js",
|
|
"skills",
|
|
"skill-data",
|
|
"!**/__pycache__",
|
|
"!**/*.pyc",
|
|
"agents",
|
|
"README.md",
|
|
"CHANGELOG.md",
|
|
"LICENSE"
|
|
],
|
|
"publishConfig": {
|
|
"access": "public"
|
|
},
|
|
"scripts": {
|
|
"build": "tsup",
|
|
"dev": "tsup --watch",
|
|
"test": "vitest run",
|
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
"test:watch": "vitest",
|
|
"test:coverage": "vitest run --coverage",
|
|
"typecheck": "tsc --noEmit",
|
|
"release": "standard-version",
|
|
"prepublishOnly": "npm run build"
|
|
},
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "git+https://github.com/Tencent/teamai-cli.git"
|
|
},
|
|
"homepage": "https://github.com/Tencent/teamai-cli#readme",
|
|
"bugs": {
|
|
"url": "https://github.com/Tencent/teamai-cli/issues"
|
|
},
|
|
"keywords": [
|
|
"ai",
|
|
"team",
|
|
"devkit",
|
|
"skills",
|
|
"sharing"
|
|
],
|
|
"author": "jeffyxu <jeffyxu@tencent.com>",
|
|
"license": "MIT",
|
|
"dependencies": {
|
|
"chalk": "^5.3.0",
|
|
"commander": "^12.1.0",
|
|
"cross-spawn": "^7.0.6",
|
|
"fflate": "^0.8.3",
|
|
"fs-extra": "^11.2.0",
|
|
"gray-matter": "^4.0.3",
|
|
"listr2": "^9.0.5",
|
|
"ora": "^8.1.0",
|
|
"semver": "^7.8.5",
|
|
"simple-git": "^3.36.0",
|
|
"smol-toml": "^1.7.1",
|
|
"tree-sitter-wasms": "0.1.13",
|
|
"web-tree-sitter": "0.25.10",
|
|
"yaml": "^2.6.0",
|
|
"zod": "^3.24.0"
|
|
},
|
|
"overrides": {
|
|
"js-yaml": "^3.15.2",
|
|
"brace-expansion@1": "1.1.18",
|
|
"brace-expansion@2": "2.1.4",
|
|
"brace-expansion@5": "5.0.9",
|
|
"nanoid": "3.3.19",
|
|
"postcss": "8.5.28",
|
|
"picomatch": "4.0.7"
|
|
},
|
|
"devDependencies": {
|
|
"@types/cross-spawn": "^6.0.6",
|
|
"@types/fs-extra": "^11.0.4",
|
|
"@types/node": "^20.17.0",
|
|
"@types/semver": "^7.8.0",
|
|
"@vitest/coverage-v8": "^3.2.7",
|
|
"opencode-ai": "1.18.23",
|
|
"standard-version": "^9.5.0",
|
|
"tsup": "^8.3.0",
|
|
"typescript": "^5.7.0",
|
|
"vitest": "^3.2.7"
|
|
},
|
|
"main": "index.js",
|
|
"directories": {
|
|
"doc": "docs",
|
|
"example": "examples",
|
|
"test": "test"
|
|
}
|
|
}
|