* 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 commit224d459c99. * refactor(lint): mark the unused options parameter of tags subscribe and unsubscribe With the #837 dry-run fix reverted out of this PR, both functions no longer read options. The underscore prefix keeps the signature and call sites unchanged, so #837 can rebase onto it by renaming the parameter back. * chore(lint): restore oxlint 1.85.0 This reverts commite2347efa. oxlint is a devDependency, so its Node requirement (^20.19.0 || >=22.12.0) never reaches users installing teamai-cli, and CI's node-version 20 resolves to the latest 20.x. Staying on 1.85.0 keeps the #836 warning counts and the planned type-aware follow-up on the same version. * docs(contributing): note the Node version npm run lint needs * docs(agents): note the Node version npm run lint needs
4.9 KiB
Contributing to TeamAI CLI
Thanks for your interest in improving TeamAI! This document explains how to get a dev environment running, how to structure changes, and how to get your PR merged.
Development Setup
git clone https://github.com/Tencent/teamai-cli.git
cd teamai-cli
npm install
Common commands
npm run build # Build with tsup → dist/
npx tsc --noEmit # Type check
npm run lint # oxlint; CI fails on any warning
npx vitest run # Run unit tests
npx vitest run --coverage
npm run test:e2e # E2E tests (optional, requires a live test repo)
npm run lint needs Node ^20.19 or >=22.12 (oxlint's requirement); the CLI itself still supports Node 20.
Running your local build
npm run build && npm link
teamai --version
Dogfood against a public team repo
All contributors should exercise pull, hooks, and recall while developing the CLI. Create a regular (not template) public team repo under teamai-hub, e.g. https://github.com/teamai-hub/teamai-cli-dev, with main branch protection (PR + review). Put only public-safe skills / rules / docs there.
In the CLI clone (not as teamai init .):
npm run build && npm link
teamai init https://github.com/teamai-hub/teamai-cli-dev --scope project --role dev
teamai pull
git status # nothing under .teamai/ or tool dirs should be staged for this repo
Pitfalls:
- Init the canonical hub URL, not a personal fork.
teamai init <url>treats that URL as the team repo; a fork diverges immediately, and GitHub push/PR today targets the configured remote (no fork-to-upstream flow). - Do not run
teamai init .. That is single-repo mode: it turns the CLI source tree into the team repo and writes scaffolding at the repo root (easy to commit by mistake). Push failed (you can push manually later)on member registration is expected without write access. Local config is still saved;teamai pullstill works.
Write access and team stats
digest / dashboard read stats/, sessions/, and members/ from the team repo. Those files are written via git, so no write ⇒ not in team stats.
Giving every internet contributor write on the hub repo is not acceptable.
| Who | Hub repo access | Required setup | In team digest |
|---|---|---|---|
| Contributors | read | init + pull |
no |
| Collaborators (after a few PRs) | write, main protected |
full, including reports | yes |
Project Layout
src/
providers/ # git hosting provider abstraction
github/ # GitHub (gh CLI or GITHUB_TOKEN)
tgit/ # Tencent TGit (gf CLI)
resources/ # per-resource-type handlers (skills, rules, docs, env, ...)
utils/ # shared helpers (git, fs, logger, prompt, ...)
*.ts # top-level command entry points (init, push, pull, ...)
See docs/providers.md for how to add a new git provider.
Making a Change
- Fork the repo and create a feature branch from the latest
origin/main. Prefer a git worktree for code changes when practical. - Write tests for your change (we target 80%+ coverage).
- Run
npx vitest run,npx tsc --noEmitandnpm run lint— all must pass. - Use conventional commits where possible:
feat:,fix:,chore:,docs:,refactor:,test:. - Open a PR with a clear description: what's the problem, what's the fix, anything reviewers should pay attention to.
Your PR also gets an informational Code Erosion report (SlopCodeBench verbosity/erosion metrics) posted as a comment — it never blocks the merge and is just there to flag creeping complexity. See docs/ci-code-erosion.md.
Coding Style
- TypeScript strict mode is on; avoid
anyunless genuinely needed. - Prefer async/await over callbacks.
- Keep commands in
src/*.tsthin — heavy lifting lives insrc/resources/orsrc/utils/. - Avoid narrating comments ("// increment counter"). Comments should explain why, not what.
Testing Guidelines
- Unit tests go in
src/__tests__/. Mirror the source file name (init.ts→init.test.ts). - Mock external I/O (git, fetch, child_process) at the module boundary.
- Avoid relying on real network access unless guarded by an env variable (like
TEAMAI_TEST_TOKEN).
Bug Reports & Feature Requests
Please file issues at github.com/Tencent/teamai-cli/issues. Include:
- What you tried to do
- What happened (error output, stack trace)
- What you expected
- Your OS, Node.js version, and
teamai --version
Security
For security issues, please do not open a public issue. Email the maintainers or use GitHub's private vulnerability reporting.
License
By contributing, you agree your contribution will be licensed under the MIT License.