Skip to content
Agentmart
Submit a tool

CLAUDE.md · vxcontrol/pentagi

CLAUDE.md from vxcontrol/pentagi: 289 lines covering Core Interaction Rules, Code Comments, Project Overview, Build & Development Commands and more.

Template by vxcontrol

IndexedMIT★ 32,256
Write a review

Install

Works with Claude Code. Read the repository before you give any tool access to your data.

curl -fsSL https://raw.githubusercontent.com/vxcontrol/pentagi/main/CLAUDE.md -o CLAUDE.md

Read it and adapt it to your project before use.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

The CLAUDE.md file that vxcontrol/pentagi (32,270 stars) keeps for coding agents. It has 289 lines in 11 sections: Core Interaction Rules; Code Comments; Project Overview; Build & Development Commands; Testing; Writing Tests; Architecture; Code Generation; Adding a New LLM Provider; Adding a New Search Engine; Utility Binaries. Use it as a model for your own project's instructions.

Use cases: prompt-engineering

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

What's in it

A CLAUDE.md file: project memory Claude Code loads at the start of every session.

For
Claude Code
Length
289 lines
File
CLAUDE.md

Sections

  1. Core Interaction Rules
  2. Code Comments
  3. Project Overview
  4. Build & Development Commands
  5. Testing
  6. Writing Tests
  7. Architecture
  8. Code Generation
  9. Adding a New LLM Provider
  10. Adding a New Search Engine
  11. Utility Binaries

First lines (shown under the repo's MIT license)

# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Core Interaction Rules

1. **English** for code, comments, documentation, commit messages and identifiers.
2. **Password Complexity Requirements**: For all password-related development (registration, password reset, API token generation, etc.), enforce the same policy in **both** backend and frontend — never rely on frontend validation alone. Source of truth, keep the two in sync: `backend/pkg/password/policy.go` (the API validator and the installer both call it) and `frontend/src/features/authentication/password-change-form.tsx` (zod schema). The policy:
   - Length 8–72 characters (72 **bytes**, the most bcrypt will hash — a longer value fails inside `bcrypt.GenerateFromPassword`, after validation).
   - A password is valid if it is **either** 16+ characters (any composition), **or** 8–15 characters containing at least 1 lowercase letter, 1 uppercase letter, 1 number, and 1 special character from `!@#$&*`.
3. **Markdown is prose, not wrapped to a width.** In a `.md` file every paragraph, list item and table row is one line, and the text is split into paragraphs where the thought changes, not where a column ends — the viewer wraps it. Hard wrapping at a fixed width belongs only to comments in code.

## Code Comments

The target: comment lines at 0–2% of a file of logic and about 18% of a file of type declarations; function bodies of hundreds of lines carry none, because the code says what it does.

**A comment earns its place only by carrying what the code cannot show**: why this decision and not the obvious one, what external constraint it works around, why the code exists at all, and — where it genuinely matters — the boundary conditions. It must help maintenance and must not cost readability.

1. **Never restate the code.** If the sentence follows from the line under it, delete it. A doc comment that only repeats an identifier's name is noise; delete it rather than reword it. A comment that repeats the `fmt.Errorf` or log message on the next line is noise too.

2. **Short and dense beats long and dense.** No filler that dilutes the point.

3. **English only.** No Russian, not even a quoted requirement.

4. **No `§`, no requirement ids like `(R37)`, no reference to a document that is not in this repository.** A planning document that is not tracked leaves every pointer into it dead. Do not just delete the token — rewrite the sentence so it stands alone, or delete the comment. "The spec says X" is not a reason; if the reason is real, state the reason. (`§` is a legitimate *string* separator in `backend/cmd/installer/wizard/models/types.go`, and in `markdown-editor-marked.ts` it is the delimiter of the anonymizer placeholder `§*Name*§` that the code reads — neither is a pointer into a document.)

5. **No archaeology.** Git holds the history. Delete "it used to be", "no longer", "before this", "now that X arrives", "was an overgeneralisation". Keep the invariant the story was there to protect. This applies to test *names* as well as test comments.

6. **No meta-commentary** about why the enum has five members rather than four, and no narrative padding that sets a scene or argues with an imagined reviewer.

7. **Rationale lives in one place** — the production code. A `_test.go` gets at most one line saying what a case pins down, and only when the test name does not already say it.

8. **When you change a comment, verify it against the code**, including that the case it describes is still reachable where it sits. A shorter comment that is wrong is worse than the long one.

Keep, and shorten only where it stays unambiguous: package doc comments; the contract of an exported identifier (units, ownership, zero/nil semantics, what the caller must hold); locking, ordering and fail-closed rationale; load-bearing formatting facts; workarounds for a library, protocol, database or browser quirk. Never touch `TODO`/`FIXME`, `//nolint`, `// eslint-disable`, `//go:generate`, `//go:embed`, build tags, or the swaggo `// @...` annotations in `pkg/server/services/` — they are machine-read, not prose.

Two kinds of comment here are machine-read even though they look like prose: the triple-quoted descriptions in `schema.graphqls`, which reach `models_gen.go`, the frontend types and every API consumer; and the comment above a `-- name:` line in `backend/sqlc/models/*.sql`, which is copied verbatim into `pkg/database/*.sql.go`. Both need regeneration after an edit.

### The GraphQL schema

`backend/pkg/graph/schema.graphqls` is a **public** artifact: introspection is on by design, so every triple-quoted description in it ships to clients and bloats the spec. A `#` comment does not ship — the parser drops it, so no client sees it and regeneration leaves every output unchanged — but it is read by everyone who edits the file. The schema carries almost no prose.

**No triple-quoted descriptions anywhere.** Not on types, not on fields, not on operations, not on enum values.

**A comment on a type, when one is genuinely needed, is a single `#` line above it, with no trailing period.** It earns its place only by saying something a client cannot get from the name — for example that the bytes are served over REST and not here, or that every mutation of the shipped template is refused.

**Fields inside types and inputs are never commented.** The name and the type are the documentation; anything more belongs in `backend/docs/` or in the Go code.

**Queries, mutations and subscriptions are never commented.** Their names say what they do; if a name does not, rename it. A single `#` line may head a *group* of operations as a navigation marker, nothing more.

**Enum values are commented only where the name does not give it**, and then about what the value leads to or how the algorithm treats it — one `#` line each.

After editing the schema, regenerate: `gqlgen`, then `swag init` — the swagger document is built from the gqlgen-generated models — and then `pnpm run graphql:generate` in `frontend/`.

**Proving a comment-only change touched no code.** For anything larger than a few files, compare the comment-free token stream of each file against its previous revision rather than reading the diff: a ~30-line Go program over `go/scanner` for `.go`, and `ts.createPrinter({removeComments:true})` over `ts.createSourceFile(...)` for `.ts`/`.tsx` (the raw `ts.createScanner` returns `Unknown` inside JSX and is not a substitute). Prove the checker itself in both directions — plant a one-token change and confirm it fails, plant a comment-only change and confirm it passes — before trusting it. A dead reference inside a string literal (a `t.Errorf` format, a `describe(...)` title) is a code token and needs its own deliberate edit.

## Project Overview

**PentAGI** is an automated security testing platform powered by AI agents. A `primary_agent` drives a flow, a `generator`/`refiner` pair plans and re-plans its subtasks, and specialist agents — `pentester`, `coder`, `searcher`, `installer`, `memorist`, `adviser`, `reflector`, `enricher`, `reporter`, `summarizer`, `assistant`, `tool_call_fixer` — execute them against LLM providers, Docker-sandboxed tools and a pgvector memory store. The authoritative list of roles is the `MsgchainType` enum in `backend/pkg/database/models.go`, mirrored as `AgentType` in the GraphQL schema.

The application is a monorepo:

- **`backend/`** — Go REST + GraphQL API server
- **`frontend/`** — React + TypeScript web UI
- **`observability/`** — optional monitoring stack configs
- **`scripts/`** — version stamping, image entrypoint, helper scripts

## Build & Development Commands

### Backend (run from `backend/`)

```bash
go mod download                              # Install dependencies
go generate ./cmd/installer/files/           # REQUIRED once per fresh clone and again after a change under
                                             #   cmd/installer/files/links or to a file linked there: fs.go,
                                             #   fs_test.go and fs/ in cmd/installer/files/ are generated and
                                             #   gitignored; the installer tests fail without them
go build -trimpath -o pentagi ./cmd/pentagi  # Quick compile; the binary reports version "ce"
go test ./...                                # Default test tier (see Testing)
golangci-lint run --timeout=5m               # Lint — config backend/.golangci.yml (v2 schema); CI pins v2.12.2
```

A binary whose reported version matters needs the ldflags CI passes: `-ldflags "-X pentagi/pkg/version.PackageVer=<ver> -X pentagi/pkg/version.PackageRev=<short-sha>"`.

### Frontend (run from `frontend/`)

```bash
nvm use                   # Node from .nvmrc — corepack does not manage Node
corepack enable           # once per Node install: activates the pnpm pinned in package.json
pnpm install
pnpm run dev              # Dev server on http://localhost:8000 (VITE_PORT / VITE_HOST / VITE_USE_HTTPS override it)
pnpm run build            # tsc -b && vite build
pnpm run typescript       # Typecheck only. Use THIS: the root tsconfig is `files: []`, so
                          #   `tsc --noEmit -p tsconfig.json` passes without checking anything
pnpm run lint             # ESLint, --max-warnings 0 over src, e2e and scripts
pnpm run prettier         # Format check (prettier:fix / lint:fix to write)
pnpm run test             # Vitest (test:watch, test:coverage)
pnpm run e2e              # Playwright, mock tier (see Testing)
pnpm run graphql:generate # Regenerate src/graphql/types.ts
```

### Docker (run from repo root)

```bash
docker compose up -d                                                            # Core services
docker compose -f docker-compose.yml -f docker-compose-observability.yml up -d  # + monitoring
docker compose -f docker-compose.yml -f docker-compose-langfuse.yml up -d       # + LLM analytics
docker compose -f docker-compose.yml -f docker-compose-graphiti.yml up -d       # + knowledge graph
docker build -t local/pentagi:latest .                                          # Image, version "ce"
./scripts/build-image.sh                                                        # Image stamped the way
                                                                                #   the pipeline stamps it
```

`docker-compose.e2e.yml` is not part of that stack: it is the isolated Tier-2 e2e backend, started under its own compose project (see the header of the file).

The full stack runs at `https://localhost:8443`. Copy `.env.example` to `.env` and fill in at minimum the database and one LLM provider key.

## Testing

**Backend.** `go test ./...` is not the whole suite — one tier sits behind a build tag and is invisible without it:

```bash
PENTAGI_TEST_DSN=postgres://… go test -p 1 -tags postgres ./pkg/database/... ./pkg/timezone/... ./migrations/
```

The tier fails when its variable is unset. `-p 1` because each package migrates into its own schema of one database, while `pg_trgm` is installed once per database: a second package running beside the first fails with `operator class gin_trgm_ops does not exist`. Under a plain `go test ./...` the suites that need a live dependency skip themselves instead — `TEST_DATABASE_URL` for the statement-timeout suite, a reachable Docker daemon for `pkg/docker` — and `go test` without `-v` does not print skips, so a green run can mean they never ran.

**Frontend.** Vitest for units; Playwright for the rest, in three tiers chosen by `E2E_TIER`:

- `mock` (default) — builds the production bundle, serves it, answers every call from a cassette;
- `local` — real backend from `docker-compose.e2e.yml` with a mock LLM (`e2e/tools/run-local-tier.sh`);
- `stand` — a deployed stand, needs `E2E_BASE_URL` and credentials.

Visual snapshots run only inside the pinned Playwright container (`pnpm run e2e:visual`); never on the host. The authoritative verdict of a run is `e2e/test-results/.last-run.json`, not the exit code of a piped command.

**The written playbooks live in `frontend/docs/`** and are worth reading before a manual pass: `e2e.md` (harness, cassettes, tiers, traps), `live-testing.md` (driving the real UI) and `list-detail-pages.md`.

## Writing Tests

Where a test lives and what it is called decide whether anyone finds it again. Each rule below is here because breaking it happened: `pkg/providers` grew 43 test files for 17 source files, one per fix, and seven of them each loaded the model catalogues to check a single property.

**Layout — one test file per source file.**

1. Tests of `x.go` live in `x_test.go`. A test for a bug, a feature or a regression goes into the test file of the source it pins — never into a new file named after the fix, the feature or the ticket. What decides the file is the code the test exists to pin, not the code it happens to execute most, and not the package it was written in: a test of `pconfig.GetOptionsForType` belongs in `pkg/providers/pconfig`.
2. The only other names: `fixtures_test.go` — test doubles shared by more than one test file of the package, and a package's catalogue of realistic sample inputs with their builders (the message chains of `pkg/cast`); `x_ext_test.go` — tests that must be `package <name>_test` because of an import cycle, with one line saying which; `x_live_test.go` — the `//go:build postgres` tier; `x_linux_test.go` and other GOOS/GOARCH suffixes — filename build constraints, never renamed away; `main_test.go` for `TestMain` and the tests of `main.go`; `example_test.go` for `Example…` functions only.
3. A helper defined in one test file and used by another is a hidden dependency: deleting the first breaks the second. Shared doubles live in `fixtures_test.go`, one per role — read it before writing another fake embedder, log recorder or Docker client.

**Names.**

4. `Test<Component>_<Unit>_<Behaviour>`: the source file in CamelCase, the one function or method under test, and what must hold — `TestTerminal_ExecCommand_KeepsPartialOutputOnTimeout`. Then `go test -run 'TestTerminal_'` selects a component and `-run 'TestTerminal_ExecCommand'` a unit. The name promises nothing the assertions do not check. Subtests are lower-case sentences.

**Before adding a test**, find the existing test of the same unit. The same setup with another input is a new row in its table, not a new function; the same input class with the same asserted outcome is a duplicate.

**Every assertion must be able to fail.** Each of these shipped here and stayed green with the behaviour it named removed:

5. A double standing in for a database, log or network write returns `ctx.Err()` once its context is done. One that ignores the context cannot tell a write on a cancelled context from a live one.
6. No expected value computed by the code under test or read from a production constant — a literal.
7. Test through the public entry — `Handle`, `Execute`, the Gin handler via `httptest`, `resolver.Mutation().X` — not the helper behind it, which stays green when the entry stops calling it.
8. Timing: lower bounds, or generous upper bounds as literals; a starved CI runner must not fail the test. Every blocking wait sits in a `select` with a timeout that fails with a reason. Parallel subtests do not share a double that mutates state.
9. `err != nil` does not say which error: use `errors.Is` or the distinguishing text.

Signals

Stars
32,256
Forks
5,002
Open issues
23
Last commit (this folder)
Oct 2, 2026 (26 days ago)
Last release
Oct 5, 2026 (23 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026
Cite this page

CLAUDE.md · vxcontrol/pentagi. Agent Mart. https://agentmart.dev/templates/vxcontrol-pentagi-claude-md (last updated 2026-10-24)