Files
ccs/docs/code-standards.md
T

8.0 KiB

CCS Code Standards

These standards describe current repository practice. Enforced configuration and tests take precedence over prose.

Source of Truth

Contract Authoritative source
TypeScript and CLI lint rules eslint.config.mjs
Formatting .prettierrc
Root commands and test entry points package.json
UI commands and dependencies ui/package.json
Commit syntax commitlint.config.cjs
Release effects .releaserc.cjs
Repository workflow CLAUDE.md and CONTRIBUTING.md

Update this document when one of those contracts changes. Do not duplicate exhaustive rule, command, target, or dependency lists here.

Design Priorities

Apply these in order:

  1. YAGNI: build only behavior required by the accepted scope.
  2. KISS: prefer a small local change over a new abstraction or dependency.
  3. DRY: share stable domain behavior, not incidental similarity.

Preserve public contracts unless the change intentionally updates them. Error messages should explain recovery, and configuration changes should remain CLI-complete with dashboard parity where the feature supports both surfaces.

Organization and Imports

  • Keep code inside the domain that owns its behavior.
  • Split files at cohesive boundaries such as types, pure utilities, lifecycle services, hooks, or view components.
  • Follow nearby naming and import patterns. New TypeScript files normally use descriptive kebab-case names.
  • There is no universal maximum directory depth.
  • Barrel exports are optional compatibility boundaries, not a repository-wide requirement. Preserve an existing barrel when consumers depend on it; use a direct import when that is the local pattern or the symbol is intentionally private.
  • Before creating a module, check whether the runtime, standard library, installed dependencies, or an existing repository utility already owns the behavior.

File Size

Two different thresholds serve different purposes:

  • 200 lines is a review heuristic. When a code file grows beyond roughly 200 lines, check whether it contains multiple responsibilities. A cohesive file may remain larger.
  • 400 lines is the current CLI lint warning. eslint.config.mjs configures max-lines as a warning for src/**/*.ts, excluding blank lines and comments.

Do not split mechanically to satisfy a number. Split when the result improves ownership, testing, reuse, or reviewability. Preserve existing exports and behavior when they are part of a consumed contract.

TypeScript and Linting

The CLI uses strict TypeScript. Current enforced CLI rules include:

  • no explicit any
  • no non-null assertions
  • no unused variables, except names intentionally prefixed with _
  • prefer-const, no-var, and strict equality

Use unknown at untrusted boundaries and narrow it before use. Keep types close to their owning domain; export types only when another module consumes them.

New generic throw new Error(...) sites are blocked by the local ccs/no-new-throw-error rule. Use the typed errors in src/errors/error-types.ts so src/errors/error-handler.ts can map failures consistently. The generated baseline in eslint-rules/throw-error-baseline.json grandfathers existing sites; do not expand it to bypass a new error design.

Terminal and Process Behavior

  • CLI terminal output is ASCII only: [OK], [!], [X], [i].
  • Respect NO_COLOR and TTY-aware output.
  • Prefer argument arrays when spawning processes. When a platform wrapper requires a shell, use the existing quoting and wrapper utilities rather than interpolating untrusted input.
  • Preserve target-specific stdio, signal, and exit-code behavior. The adapter contract lives in src/targets/target-adapter.ts.

Target Adapters

Runtime target behavior is divided across:

Concern Source
Target names, aliases, persistence src/targets/target-metadata.ts
Selection priority and flag parsing src/targets/target-resolver.ts
Adapter contract src/targets/target-adapter.ts
Registration and lookup src/targets/target-registry.ts
Startup registration src/ccs.ts

A target change should update metadata, implementation, registration, tests, help text, and public documentation as applicable. Do not copy the target list into new guides; link to metadata when maintainers need the current set.

Configuration and Test Isolation

  • Treat persisted environment values as strings.
  • Route CCS paths through getCcsDir() in src/utils/config-manager.ts.
  • Never run tests against a contributor's real ~/.ccs/ or ~/.claude/. Set CCS_HOME to a temporary directory.
  • Preserve documented configuration and profile-resolution priority. Add a focused test when changing precedence or fallback behavior.
  • Treat Docker service ccs and network ccs-net as public contracts; see CONTRIBUTING.md.

Dashboard Code

  • Keep page orchestration in ui/src/pages/, reusable UI in ui/src/components/, server-state access in ui/src/hooks/, and shared helpers in ui/src/lib/ when that matches the owning domain.
  • Preserve unsaved input across background refreshes. Destructive replacement of dirty state requires an explicit user action or confirmation.
  • Use the existing API and query helpers before adding a new data-access layer.
  • Keep accessibility, keyboard interaction, responsive layout, loading, empty, and error states in scope for user-facing changes.
  • Locale codes and normalization are owned by ui/src/lib/locales.ts; translations are wired through ui/src/lib/i18n.ts.

The UI has its own ESLint, TypeScript, Prettier, and Vitest configuration. Verify commands against ui/package.json.

Tests and Quality Gates

The root TypeScript suites run with Bun's test runner. The dashboard uses Vitest. Native shell and PowerShell probes cover platform-specific behavior. See tests/README.md for ownership and commands.

Run the smallest relevant test first, then the normal gate:

bun run format
bun run lint:fix
bun run validate

Before review or merge confidence:

bun run validate:ci-parity

For dashboard changes:

cd ui
bun run format
bun run validate
bun run test:run

Do not weaken or skip a failing check to make a change pass. If a full gate cannot run, report the exact focused checks completed and the blocker.

Commits and Releases

Commitlint accepts conventional types defined in commitlint.config.cjs, with a 100-character header limit. Use focused subjects such as:

fix(doctor): handle missing config
feat(cliproxy): add provider quota check
docs(contributing): clarify validation

Semantic-release owns versions, changelog entries, tags, npm publishing, and GitHub releases. Do not bump versions, tag, or publish manually. Release effects for each branch and commit type are defined in .releaserc.cjs.

Documentation Triggers

Update the owning guide when a change affects behavior, commands, setup, architecture, security posture, public contracts, or future maintainer decisions. Start at docs/README.md. Public CLI, provider, configuration, installation, or workflow changes also require the matching page in the separate kaitranntt/ccs-docs repository; see the docs index for checkout guidance.

Prefer source links over copied trees and metrics. Remove stale sections rather than leaving TODO markers.