Files
Saul Moro 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 224d459c99.

* 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 commit e2347efa. 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
2026-09-26 19:56:03 +08:00

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 pull still 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

  1. Fork the repo and create a feature branch from the latest origin/main. Prefer a git worktree for code changes when practical.
  2. Write tests for your change (we target 80%+ coverage).
  3. Run npx vitest run, npx tsc --noEmit and npm run lint — all must pass.
  4. Use conventional commits where possible: feat:, fix:, chore:, docs:, refactor:, test:.
  5. 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 any unless genuinely needed.
  • Prefer async/await over callbacks.
  • Keep commands in src/*.ts thin — heavy lifting lives in src/resources/ or src/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.