Skip to content

Contributing

This page is the public contributor guide for AICodeReviewer. It covers the repository layout, local development setup, the test and validation matrix, and the common contribution workflows. The repository’s AGENTS.md holds the always-on rules, guardrails, environment notes, and the list of known codebase pitfalls to avoid reintroducing — read it before larger changes.

Path Purpose
packages/* Runtime TypeScript packages (CLI, core, server, agents, sandbox, outputs, mcp-output, llm, vcs, store, eval). Managed with pnpm workspaces and TypeScript project references.
docs/site This documentation site (Astro Starlight, English + 简体中文). An isolated workspace package; not part of the runtime.
docs/ (other) Topical reference modules (e.g. output channels) consulted while writing these pages.
example/ Deployment sample: config.yaml, .env.sample, Compose stack, trigger scripts.
deploy/ Dockerfile, deploy.sh, and related deployment assets.
eval/ Permanent eval CLI test fixtures.
AGENTS.md Always-on contributor guidance, guardrails, and known codebase pitfalls.
.agents/skills/ Repeatable workflow skills (audit, deployment, maintenance, etc.).

Requirements:

  • Node.js >= 22 for the runtime (better-sqlite3 13 requires it; the deployment image uses Node 24 LTS userspace).
  • Node.js >= 23.6 for docs:build / docs:check; the source-backed config validator relies on native TypeScript stripping. The docs CI job uses Node 24.
  • pnpm.
Terminal window
# From the repository root
pnpm install
pnpm build

After the final edit, run every applicable gate before proposing a change and confirm it discovers the expected files or tests. On Linux/CI, pnpm run ci is the final runtime gate; on Windows PowerShell invoke the Node binaries directly.

Step Linux/CI Windows PowerShell
ESLint pnpm lint node node_modules/eslint/bin/eslint.js . --max-warnings=0
Typecheck pnpm typecheck node node_modules/typescript/bin/tsc -b tsconfig.json --pretty false
Unit tests pnpm test node node_modules/vitest/vitest.mjs run --coverage
Markdown lint pnpm markdownlint node node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs
Build pnpm build cmd /c "pnpm build"
Eval fixture validation pnpm eval:validate (after build) node packages/cli/dist/index.js eval --validate-only
Docs build pnpm docs:build pnpm docs:build

pnpm eval:validate runs aicr eval --validate-only, which checks eval/*.json shape and expected-problem contracts only — no LLM, no config secrets. A full aicr eval run loads config and calls the LLM, so keep it as a separate environment-specific benchmark job.

Changes that affect config shape, agent adapters, MCP tool contracts, output rendering, deployment behavior, or public workflow must update the matching docs, example/config.yaml, and example/README.md in the same change.

These tests can use disposable local services without production credentials:

Variable Requirement and coverage
AICR_SVN_TEST_EXECUTABLE Absolute path to svn, with svnadmin and svnserve in the same directory. Enables real repository metadata tests and a post-commit hook → authenticated HTTP → SQLite scheduling test.
AICR_REDIS_TEST_URL URL of a local test Redis. Enables automatic-commit storage and model-catalog persistence tests; the catalog test uses its own random key prefix.
AICR_GITEA_TEST_URL / AICR_GITEA_TEST_TOKEN Disposable loopback HTTP Gitea and its temporary admin token. Tests create private repositories and users, publish issues, then read persisted assignees. Both unset means skipped; incomplete configuration fails.

Set the variables before running the test command above. Without them, the corresponding integration tests are skipped. SVN fixtures live under build/tmp/ and the hook test stops its own daemon. These tests do not call an LLM or publish reviews to remote systems. Deployment-specific authentication and networking still need separate acceptance.

For Gitea, run this from the checkout root in Linux or WSL Debian with rootless Podman, curl, node and timeout available:

Terminal window
bash tests/services/with-gitea.sh \
pnpm exec vitest run packages/outputs/test/gitea-assignment-live.test.ts --maxWorkers=1

The wrapper pins Gitea 1.25.4 with SQLite, limits it to 1 CPU / 512 MiB and binds a random loopback port. It uses a fresh directory beneath ~/workspace/github/atframework (override with AICR_ACCEPTANCE_ROOT), supplies the two fixture variables, and removes its container, data and newly downloaded image on exit. The service also has a 900-second lifetime limit. Logs stay under build/logs/gitea-*. After interruption, verify owned resources were removed; SIGKILL cannot run directory cleanup. These results cover the pinned Gitea version.

packages/core/test/config-examples.test.ts always validates the deployment config and configuration YAML blocks across repository documentation, including both locales. Malformed configuration blocks fail instead of being skipped.

The SVN wrapper uses a pinned Debian image and Subversion 1.14.5-3, with 1 CPU / 256 MiB, a random loopback port and the same 900-second cleanup policy. It provides AICR_SVN_TEST_URL; the test process needs svn on PATH. The tests check auxiliary repository content, diff and failure cleanup, then run analysis and publication through the orchestrator with a deterministic model fixture.

Terminal window
bash tests/services/with-svn.sh \
pnpm exec vitest run packages/vcs/test/svn-context-live.test.ts \
packages/server/test/svn-analysis-live.test.ts --maxWorkers=1

For PostgreSQL SCRAM/TLS/roles, Redis TLS/ACL/AOF and SVN HTTPS/authz/hooks, run the deployment fixture. It checks persisted data after a container restart, uses random loopback ports and shares 1 CPU / 512 MiB across the services. It removes its container, volume and temporary directory on exit. Debian package versions are recorded in build/logs/deployment-versions.log.

Terminal window
bash tests/services/with-deployment-services.sh

An optional child command receives AICR_PG_TEST_URL, AICR_REDIS_TEST_URL, AICR_REDIS_OOM_TEST_URL and NODE_EXTRA_CA_CERTS. Certificate verification stays enabled; OOM injection uses a separate Redis process. Serialize suites sharing PG:

Terminal window
bash tests/services/with-deployment-services.sh \
pnpm exec vitest run --coverage --maxWorkers=1

Real-account tests skip when their variable group is absent; incomplete groups fail. They never read a credential file automatically. Use a test group/account.

Required variables Test and optional settings
AICR_FEISHU_TEST_APP_ID, AICR_FEISHU_TEST_APP_SECRET, AICR_FEISHU_TEST_RECEIVE_ID packages/outputs/test/feishu-app-live.test.ts: read members/profiles, send one card and recall it. AICR_FEISHU_TEST_DIRECTORY_CHAT_ID selects another source group; AICR_FEISHU_TEST_MENTION_OPEN_ID opts into notifying one approved member.
AICR_ZHIPU_TEST_BASE_URL, AICR_ZHIPU_TEST_API_KEY packages/llm/test/providers-live.test.ts: glm-5.3-flash; AICR_ZHIPU_TEST_KIND selects openai_compatible (default) or anthropic.
AICR_KIMI_TEST_BASE_URL, AICR_KIMI_TEST_API_KEY Same test, kimi-for-coding; AICR_KIMI_TEST_KIND selects the protocol.

For anthropic, the base URL is the protocol root without /v1. LLM cases make one request per enabled provider, with a 60-second deadline, 256 output tokens, thinking disabled and no automatic retries. They check an answer and usage, not review quality or billing. Logs contain counts and usage, never credentials, profiles or raw provider errors. Feishu recall failures fail the run; after a forced interruption, check the test group for a remaining card.

If you maintain development/secret/secret.yaml, the explicit local helper requires yq v4 and reads only the selected fields into the test child’s environment:

Terminal window
node tests/services/with-local-secrets.mjs feishu
node tests/services/with-local-secrets.mjs zhipu
node tests/services/with-local-secrets.mjs kimi
node tests/services/with-local-secrets.mjs zhipu anthropic
node tests/services/with-local-secrets.mjs kimi anthropic

The field groups are .channel.feishu_app.{app_id,app_secret,receive_id}, .llm.provider.zhipu.{baseURL,token} and .llm.provider.kimi_coding_backup.{baseURL,token}. CI supplies environment variables directly. Keep these calls separate from routine coverage runs.

  1. Create the package directory under packages/<name>/ with its own package.json, tsconfig.json, src/, and test/.
  2. Add the package to pnpm-workspace.yaml (the workspace already globs packages/*, so this is usually automatic).
  3. Add a TypeScript project reference from the root tsconfig.json and from any package that consumes it; add a reference back from the new package’s tsconfig.json to its dependencies.
  4. Add at least a test/index.test.ts so the package has a test surface, even if it only exports a constant.
  5. Add the new onlyBuiltDependencies entry to pnpm-workspace.yaml if the package introduces a native module (pnpm 10 gates native builds behind onlyBuiltDependencies).

The Zod schema in packages/core/src/config.ts is the source of truth.

  1. Update the schema (and any superRefine cross-field validation).
  2. Add or update a test in packages/core/test/config.test.ts.
  3. Update example/config.yaml with a commented example.
  4. Update the relevant narrative page under docs/site/src/content/docs/.../configuration/ and the field table in Configuration fields — in both locales. pnpm docs:build fails if the table misses a schema field, documents an invented field, or the two locales drift apart, so the gate tells you what is missing.
  5. If the field changes runtime behavior, update example/README.md and the matching topical doc.

Workspace config files cannot write system-level fields; respect the cache / defaults / instances three-part shape and the global → workspace-default → workspace-instance override order.

  1. Implement the dispatcher in packages/outputs/src/ and register it in the output registry. The channel kind is a free-form string constrained by the registry (not a closed enum).
  2. Add built-in Handlebars templates for the problem and summary variants under the template engine.
  3. Add tests, including the IM-markdown transformer if the channel is an IM bot (table regexes must not use the g flag with .test()).
  4. Document the channel in Output channels and add its fields to Configuration fields.
  5. Update example/config.yaml with a commented example.

See Output channels for the problem schema, summary schema, channel mapping, and the no-problems policy that every channel must respect.

The docs site is bilingual (English under .../en/, 简体中文 under .../zh-cn/). Every user-facing page exists in both locales; keep config keys, commands, paths, field names, and enum values identical across locales.

  • Build and validate locally with pnpm docs:build. The build runs six validation gates: the public/internal boundary (pages under src/content/docs/ must not reference the internal AI/roadmap documentation tree or carry migration-source maintenance notes), config-field coverage of the Zod schema, CLI command/flag consistency with packages/cli, internal link/anchor resolution including sidebar coverage, SEO metadata (every page needs a non-empty title/description within length bounds, plus the committed robots.txt and 1200x630 og-image.png), and bilingual consistency (same page set in both locales, identical code-fence counts and machine tokens — config keys, env vars, flags, paths — and no banned filler words).
  • Sidebar slugs omit the index segment (e.g. troubleshooting/index.md has slug troubleshooting). Frontmatter template only accepts doc or splash; Starlight social is an array of link items.
  • Content pages use .md. The two landing pages (en/index.mdx, zh-cn/index.mdx) use .mdx so they can render Starlight components (hero frontmatter plus Card, CardGrid, LinkCard, Steps, Aside). MDX is provided by Starlight with no extra integration; components never render in plain .md. The public-content validator scans both .md and .mdx.
  • Cross-links use locale-prefixed paths (/en/..., /zh-cn/...).

When you change a config shape, output contract, or runtime behavior, update both locales’ relevant pages in the same change.

  • Keep edits minimal and surgical; do not weaken lint, typecheck, test, or markdown gates to land a change.
  • All temporary task artifacts (scratch scripts, debug logs, one-off reports, benchmark output) go under build/, never in the repository root, eval/, or a package directory.
  • Public/shared modules (packages/cli/src, ReviewEvent, template context) must stay platform-neutral — import canonical schemas from @aicr/core and keep provider/channel-specific names inside config contracts, docs, tests, and platform-specific adapters.

For the full, always-on contributor rules — including the numbered list of known codebase pitfalls to avoid reintroducing — read AGENTS.md at the repository root.