CLAUDE.md and GEMINI.md
This file provides guidance to Claude Code (claude.ai/code) and also Gemini CLI (https://github.com/google-gemini/gemini-cli) when working with code in this repository.
CoCalc Source Repository
This is the source code of CoCalc in a Git repository
It is a complex JavaScript/TypeScript SaaS application
CoCalc is organized as a monorepository (multi-packages) in the subdirectory "./src/packages"
The packages are managed as a pnpm workspace in "./src/packages/pnpm-workspace.yaml"
Code Style
Everything is written in TypeScript code
Indentation: 2-spaces
Run
prettier -w [filename]after modifying a file (ts, tsx, md, json, ...) to format it correctly.All .js and .ts files are formatted by the tool prettier
Add suitable types when you write code
Follow DRY principles!
TypeScript: prefer
var1 ?? var2for fallbacks. only usevar1 || var2in explicit or-chains or when necessary.Variable name styles are
camelCasefor local andFOO_BARfor global variables. React Components and Classes areFooBar. If you edit older code not following these guidelines, adjust this rule to fit the file's style.Some older code is JavaScript or CoffeeScript, which will be translated to TypeScript
Use ES modules (import/export) syntax, not CommonJS (require)
Organize the list of imports in such a way: installed npm packages are on top, newline, then are imports from @cocalc's code base. Sorted alphabetically.
Colors: Always use the
COLORSdictionary from@cocalc/util/themefor all color values. Never hardcode colors like#f0f0f0orrgb(...). Import withimport { COLORS } from "@cocalc/util/theme";and use predefined constants likeCOLORS.GRAY_M,COLORS.GRAY_L,COLORS.GRAY_LL, etc.Backend Logging: Use
getLoggerfrom@cocalc/project/loggerfor logging in backend code. Do NOT useconsole.log. Example:const L = getLogger("module:name").debug;
Development Commands
Essential Commands
pnpm build-dev- Build all packages for developmentpnpm clean- Clean allnode_modulesanddistdirectoriespnpm test- Run full test suitepnpm depcheck- Check for dependency issuespython3 ./src/scripts/check_npm_packages.py- Check npm package consistency across packagesprettier -w [filename]to format the style of a file after editing itAfter creating a file, run
git add [filename]to start tracking it
Package-Specific Commands
cd src/packages/[package] && pnpm build- Build and compile a specific packageFor src/packages/next and src/packages/static, run
cd src/packages/[package] && pnpm build-dev
cd src/packages/[package] && pnpm test- Run tests for a specific packageYou can pass file arguments:
cd src/packages/[package] && pnpm test -- [test-file]to run a single test file
IMPORTANT: When modifying packages like
utilthat other packages depend on, you must runpnpm buildin the modified package before typechecking dependent packagesIMPORTANT: When modifying colors in
src/packages/util/theme.ts, runcd src/packages/frontend && pnpm update-color-schemeto regenerate the SASS color variables insrc/packages/frontend/_colors.sass
Workspace Management (src/workspaces.py)
The root-level src/workspaces.py script orchestrates operations across all packages in the monorepo. Use it instead of running raw pnpm commands when working across the workspace:
python3 src/workspaces.py install- Install dependencies for all packages (use after updating package.json files)python3 src/workspaces.py build- Build all packages that have changedpython3 src/workspaces.py clean- Delete dist and node_modules folderspython3 src/workspaces.py version-check- Check dependency version consistency across all packagespython3 src/workspaces.py test- Run tests for all packages
IMPORTANT: After updating dependencies in any package.json, run python3 src/workspaces.py version-check to ensure consistency, then python3 src/workspaces.py install to update the lockfile and install.
Development
IMPORTANT: Always run
prettier -w [filename]immediately after editing any .ts, .tsx, .md, or .json file to ensure consistent stylingIMPORTANT: In tests and code comments, use only generic names, email addresses, and company names. Do not include customer or real-world identifiers, except for
Sagemath, Inc.or when the developer explicitly says otherwise.
When Working on Frontend Code
After making changes to files in src/packages/frontend:
Typecheck: Run
cd src/packages/frontend && pnpm tsc --noEmitto check for TypeScript errorsBuild: Run
cd src/packages/static && pnpm build-devto compile the frontend for testing
DO NOT run pnpm build in src/packages/frontend - it won't work as expected for frontend development.
When Working on Other Packages
After TypeScript changes, run
pnpm buildin the relevant package directory
Architecture Overview
For detailed architecture documentation, see src/docs/:
System Overview — High-level architecture and data flow
Frontend — React app, state management, client layer
Conat — Messaging system: DKV, PubSub, request/response
Hub & Server — Central server, API dispatch, database
Next.js — SSR pages, REST API routes, conat bridge
External API — Python client, HTTP endpoints, call flow
Project Daemon — Per-project services and conat integration
Package Structure
CoCalc is organized as a monorepo with key packages:
frontend - React/TypeScript frontend application using Redux-style stores and actions
backend - Node.js backend services and utilities
hub - Main server orchestrating the entire system
database - PostgreSQL database layer with queries and schema
util - Shared utilities and types used across packages
comm - Communication layer including WebSocket types
conat - CoCalc's container/compute orchestration system
sync - Real-time synchronization system for collaborative editing
project - Project-level services and management
static - Static assets and build configuration
next - Next.js server components
Key Architectural Patterns
Frontend Architecture
Redux-style State Management: Uses custom stores and actions pattern (see
src/packages/frontend/app-framework/actions-and-stores.ts)TypeScript React Components: All frontend code is TypeScript with proper typing
Modular Store System: Each feature has its own store/actions (AccountStore, BillingStore, etc.)
WebSocket Communication: Real-time communication with backend via WebSocket messages
Authentication Waiting: When frontend code needs to wait for user authentication, use
redux.getStore("account").async_wait({ until: () => store.get_account_id() != null, timeout: 0 })to wait indefinitely until authentication completesConat DKV Usage: For key-value storage with real-time sync, use
webapp_client.conat_client.dkv({ account_id, name: "store-name" })to get a distributed key-value store that syncs across sessions
Backend Architecture
PostgreSQL Database: Primary data store with sophisticated querying system
WebSocket Messaging: Real-time communication between frontend and backend
Conat System: Container orchestration for compute servers
Event-Driven Architecture: Extensive use of EventEmitter patterns
Microservice-like Packages: Each package handles specific functionality
Database Access: Use
getPool()from@cocalc/database/poolfor direct database queries in hub/backend code. Example:const pool = getPool(); const { rows } = await pool.query('SELECT * FROM table WHERE id = $1', [id]);Hub Migration Functions: Migration functions in hub should be designed to run once at startup, use batch processing with delays between batches to avoid database saturation
Communication Patterns
WebSocket Messages: Primary communication method (see
src/packages/comm/websocket/types.ts)Database Queries: Structured query system with typed interfaces
Event Emitters: Inter-service communication within backend
REST-like APIs: Some HTTP endpoints for specific operations
API Schema: API endpoints in
src/packages/next/pages/api/v2/use Zod schemas insrc/packages/next/lib/api/schema/for validation. These schemas must be kept in harmony with the TypeScript types sent from frontend applications usingapiPost(insrc/packages/next/lib/api/post.ts) orapi(insrc/packages/frontend/client/api.ts). When adding new fields to API requests, both the frontend types and the API schema validation must be updated.Conat Frontend → Hub Communication: CoCalc uses a custom distributed messaging system called "Conat" for frontend-to-hub communication:
Frontend ConatClient (
src/packages/frontend/conat/client.ts): Manages WebSocket connection to hub, handles authentication, reconnection, and provides API interfacesCore Protocol (
src/packages/conat/core/client.ts): NATS-like pub/sub/request/response messaging with automatic chunking, multiple encoding formats (MsgPack, JSON), and delivery confirmationHub API Structure (
src/packages/conat/hub/api/): Typed interfaces for different services (system, projects, db, purchases, jupyter) that map function calls to conat subjectsMessage Flow: Frontend calls like
hub.projects.setQuotas()→ ConatClient.callHub() → conat request to subjecthub.account.{account_id}.api→ Hub API dispatcher → actual service implementationAuthentication: Each conat request includes account_id and is subject to permission checks at the hub level
Subjects: Messages are routed using hierarchical subjects like
hub.account.{uuid}.{service}orproject.{uuid}.{compute_server_id}.{service}
CoCalc Conat Hub API Architecture
API Method Registration Pattern:
Registry:
src/packages/conat/hub/api/projects.tscontainsexport const projects = { methodName: authFirstRequireAccount }Implementation:
src/packages/server/conat/api/projects.tscontainsexport async function methodName() { ... }Flow: Python client
@api_method("projects.methodName")→ POST/api/conat/hub→hubBridge()→ conat subjecthub.account.{account_id}.api→ registry lookup → implementation
Example - projects.createProject:
Python:
@api_method("projects.createProject")decoratorHTTP:
POST /api/conat/hub {"name": "projects.createProject", "args": [...]}Bridge:
hubBridge()routes to conat subjectRegistry:
src/packages/conat/hub/api/projects.ts: createProject: authFirstRequireAccountImplementation:
src/packages/server/conat/api/projects.ts: export { createProject }→@cocalc/server/projects/create
Key Technologies
TypeScript: Primary language for all new code
React: Frontend framework
PostgreSQL: Database
Node.js: Backend runtime
WebSockets: Real-time communication
pnpm: Package manager and workspace management
Jest: Testing framework
SASS: CSS preprocessing
Database Schema
Comprehensive schema in
src/packages/util/db-schemaQuery abstractions in
src/packages/database/postgres/Type-safe database operations with TypeScript interfaces
Testing
Jest: Primary testing framework
ts-jest: TypeScript support for Jest
jsdom: Browser environment simulation for frontend tests
Test files use
.test.tsor.spec.tsextensionsEach package has its own jest.config.js
Playwright MCP: For interactive browser testing of the frontend UI, see
src/packages/frontend/test/agent-playwright-testing.md— covers the dev server setup, build-test loop, UI layout, and testing patterns for the file explorer and flyout panels. Always ask the developer for current dev account credentials.
Import Patterns
Use absolute imports with
@cocalc/prefix for cross-package importsExample:
import { cmp } from "@cocalc/util/misc"Type imports:
import type { Foo } from "./bar"Destructure imports when possible
Development Workflow
Frontend changes: After editing
src/packages/frontend, typecheck withcd src/packages/frontend && pnpm tsc --noEmit, then build withcd src/packages/static && pnpm build-devOther package changes: After TypeScript changes, run
pnpm buildin the relevant package directoryDatabase must be running before starting hub
Hub coordinates all services and should be restarted after changes
Use
pnpm clean && pnpm build-devwhen switching branches or after major changes
Workflow
Be sure to build when you're done making a series of code changes
Prefer running single tests, and not the whole test suite, for performance
Git Workflow
Never modify a file when in the
masterormainbranchAll changes happen through feature branches, which are pushed as pull requests to GitHub
When creating a new file, run
git add [filename]to track the file.The first line of a commit message must follow the pattern:
[package]/[region]: [1-line description]. e.g.frontend/latex: fix PDF preview syncorfrontend/frame-editor: add drag-and-drop support. The package is the subdirectory undersrc/packages/and the region is the feature area within that package.When pushing a new branch to Github, track it upstream. e.g.
git push --set-upstream origin feature-foofor branch "feature-foo".Branch naming: New branches should follow the pattern
[some-key-words]-[issue-number]. e.g.fix-invite-email-signup-link-8757for issue #8757.
React-intl / Internationalization (i18n)
CoCalc uses react-intl for internationalization with SimpleLocalize as the translation platform.
Architecture Overview
Library: Uses
react-intllibrary withdefineMessages()anddefineMessage()Default Language: English uses
defaultMessagedirectly - no separate English translation filesSupported Languages: 19+ languages including German, Chinese, Spanish, French, Italian, Dutch, Russian, Japanese, Portuguese, Korean, Polish, Turkish, Hebrew, Hindi, Hungarian, Arabic, and Basque
Translation Platform: SimpleLocalize with OpenAI GPT-4o for automatic translations
Translation ID Naming Convention
Translation IDs follow a hierarchical pattern: [directory].[subdir].[filename].[aspect].[label|title|tooltip|...]
Examples:
labels.account- for common UI labelsaccount.sign-out.button.title- for account sign-out dialogcommand.generic.force_build.label- for command labels
Usage Patterns
TSX Components:
<FormattedMessage id="..." defaultMessage="..." />Data Structures:
defineMessage({id: "...", defaultMessage: "..."})Programmatic Use:
useIntl()hook +intl.formatMessage()Non-React Contexts:
getIntl()function
Translation Workflow
For new translation keys:
Add the translation to source code (e.g.,
src/packages/frontend/i18n/common.ts)Run
pnpm i18n:extract- updatesextracted.jsonfrom source codeRun
pnpm i18n:upload- sends new strings to SimpleLocalizeNew keys are automatically translated to all languages
Run
pnpm i18n:download- fetches translationsRun
pnpm i18n:compile- compiles translation files
For editing existing translation keys: Same flow as above, but before 3. i18n:upload, delete the key. Only new keys are auto-translated. pnpm i18n:delete [id].
Translation File Structure
src/packages/frontend/i18n/README.md- detailed documentationsrc/packages/frontend/i18n/common.ts- shared translation definitions (labels, menus, editor, jupyter, etc.)src/packages/frontend/i18n/extracted.json- auto-extracted messages from source codesrc/packages/frontend/i18n/trans/[locale].json- downloaded translations from SimpleLocalizesrc/packages/frontend/i18n/trans/[locale].compiled.json- compiled translation files for runtimesrc/packages/frontend/i18n/index.ts- exports and locale loading logic
Ignore
Ignore files covered by
.gitignoreIgnore everything in
node_modulesordistdirectoriesIgnore all files not tracked by Git, unless they are newly created files
CoCalc Python API Client Investigation
Overview
The src/python/cocalc-api/ directory contains a uv-based Python client library for the CoCalc API, published as the cocalc-api package on PyPI.
It also contains a test framework (src/python/cocalc-api/tests/README.md) and an MCP client (src/python/cocalc-api/src/cocalc_api/mcp/README.md). For convenience, a src/python/cocalc-api/Makefile exists.
Client-Server Architecture Investigation
API Call Flow
cocalc-api Client (Python) → HTTP POST requests
Next.js API Routes (
/api/conat/{hub,project}) → Bridge to conat messagingConatClient (server-side) → NATS-like messaging protocol
Hub API Implementation (
src/packages/conat/hub/api/) → Actual business logic
Endpoints Discovered
Hub API: POST /api/conat/hub
Bridge:
src/packages/next/pages/api/conat/hub.ts→hubBridge()→ conat subjecthub.account.{account_id}.apiImplementation:
src/packages/conat/hub/api/projects.tsAvailable Methods:
createProject,start,stop,setQuotas,addCollaborator,removeCollaborator, etc.
Project API: POST /api/conat/project
Bridge:
src/packages/next/pages/api/conat/project.ts→projectBridge()→ conat project subjectsImplementation:
src/packages/conat/project/api/(system.ping, system.exec, system.jupyterExecute)
important-instruction-reminders
Do what has been asked; nothing more, nothing less.
ALWAYS prefer editing an existing file to creating a new one.
NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
ALWAYS ask questions if something is unclear. Only proceed to the implementation step if you have no questions left.
When modifying a file with a copyright banner at the top, make sure to fix/add the current year to indicate the copyright year.