# AGENTS.md

Instructions for **AI coding assistants** (Cursor, Claude Code, and similar) working in this repository.

## Contributing guide vs this file

| | Purpose |
|---|--------|
| **[CONTRIBUTING.md](CONTRIBUTING.md)** | **Contributing guide for all contributors**—humans and agents. Workflow, critical rules, testing, Pascal code style, formatting, build/run reference, documentation index. If it affects what may be merged, it belongs there. |
| **This file (`AGENTS.md` / `CLAUDE.md`)** | **Agent-only** context: how assistants should operate in this repo, where to read first, and what not to duplicate. It does **not** replace CONTRIBUTING. |

Assistants should treat CONTRIBUTING as authoritative for contribution requirements. Use this file for assistant-specific expectations and navigation, not a second copy of CONTRIBUTING.

## Expectations for assistants

- **Read [CONTRIBUTING.md](CONTRIBUTING.md)** before substantive edits—especially [Critical rules](CONTRIBUTING.md#critical-rules) and [Code style](docs/contributing/code-style.md).
- **Before creating or restructuring units**, you **must** complete these steps before writing any code:
  1. Read [Source Directory Layout](docs/contributing/code-style.md#source-directory-layout-and-namespacing) — determine whether the unit belongs in `source/shared/` or `source/units/` and why.
  2. Read [Test Principles](docs/testing.md#test-principles) — understand what to test (public API only) and what not to test (private helpers, implementation details).
  3. State the public API of the new unit and which directory it belongs in. Do not proceed until this is confirmed.
  4. Keep implementation details private; test only public APIs.
- **Run verification yourself** when the environment allows (tests, format check); do not only tell the human what to run unless execution is impossible.
- **Match the project's workflow**: branch from `main`, focused diffs, tests and docs updated per CONTRIBUTING.
- **Do not paste large chunks of CONTRIBUTING into this file** when CONTRIBUTING changes—edit CONTRIBUTING instead, and keep AGENTS short.

## Quick checks

```bash
./build.pas testrunner && ./build/GocciaTestRunner tests --asi --unsafe-ffi  # after substantive changes
./build.pas bundler && ./build/GocciaBundler example.js  # build and run the bundler
./format.pas --check # before push / PR
```

## Quick reference

### Build commands

```bash
./build.pas # Clean + dev build of everything (default)
./build.pas --dev loader # Dev build of GocciaScriptLoader
./build.pas --dev loaderbare # Dev build of GocciaScriptLoaderBare
./build.pas --prod # Clean + production build of everything
./build.pas --prod loader repl # Production build of specific components
./build.pas loader # Dev build (--dev is the default)
./build.pas loaderbare # Dev build of the bare ScriptLoader
./build.pas testrunner # Dev build of GocciaTestRunner
./build.pas benchmarkrunner # Dev build of GocciaBenchmarkRunner
./build.pas bundler # Dev build of GocciaBundler
./build.pas tests # Dev build of Pascal unit tests
./build.pas clean # Clean stale artifacts only (no build)
./build.pas clean loader # Clean, then dev build of ScriptLoader
```

### Run commands

```bash
./build/GocciaScriptLoader example.js # Execute a script (interpreted)
./build/GocciaScriptLoader example.js --mode=bytecode # Execute via bytecode VM
./build/GocciaScriptLoader example.js --import-map=imports.json # Execute with an explicit import map
./build/GocciaScriptLoader example.js --config=./configs/strict.toml # Load root config from an explicit file (skips auto-discovery)
./build/GocciaScriptLoader example.js --config=./configs/ # Load root config from a directory (looks up goccia.{toml,json5,json}, no walk-up)
./build/GocciaScriptLoader example.js --alias @/=./src/ --alias config=./config/default.js # One-off import-map-style aliases
./build/GocciaScriptLoader out.gbc # Load and execute .gbc bytecode
printf "const x = 2 + 2; x;" | ./build/GocciaScriptLoader # Execute stdin source
./build/GocciaScriptLoader example.js --coverage # Execute with line and branch coverage
./build/GocciaScriptLoader example.js --coverage --coverage-format=lcov --coverage-output=coverage.lcov # Coverage with lcov output
./build/GocciaScriptLoader example.js --coverage --coverage-format=json --coverage-output=coverage.json # Coverage with JSON output
./build/GocciaScriptLoader example.js --asi # Execute with automatic semicolon insertion
./build/GocciaScriptLoader example.js --strict-types # Enforce type annotations at runtime (interpreter and bytecode)
./build/GocciaScriptLoader example.js --source-type=module # Load entry as a module (top-level this is undefined; default is script)
./build/GocciaScriptLoader example.js --output=json # Execute with structured JSON output
./build/GocciaScriptLoader example.js --output=compact-json # Execute with structured JSON output, omitting build, memory, stdout, and stderr
./build/GocciaScriptLoader example.js --print # Print the script's last value (incl. undefined; mirrors node -p / bun --print)
./build/GocciaScriptLoader example.js --profile=opcodes # Opcode histogram, pair frequency, scalar hit rate (bytecode)
./build/GocciaScriptLoader example.js --profile=functions # Function self-time, allocations (bytecode)
./build/GocciaScriptLoader example.js --profile=all # All profiling data (bytecode)
./build/GocciaScriptLoader example.js --profile=all --profile-output=profile.json # Profile with JSON export
./build/GocciaScriptLoader example.js --profile=functions --profile-format=flamegraph --profile-output=flamegraph.txt # Flame graph export
./build/GocciaScriptLoader example.jsx --source-map --mode=bytecode # Write .map source map alongside execution
./build/GocciaScriptLoader example.js --max-memory=10485760 # Execute with 10 MB GC heap limit (RangeError on exceed)
./build/GocciaScriptLoader example.js --max-instructions=1000000 --mode=bytecode # Abort after 1M bytecode instructions
./build/GocciaScriptLoader example.js --unsafe-ffi # Execute with FFI enabled
./build/GocciaScriptLoader example.js --allowed-host=api.example.com # Execute with fetch restricted to a single host
./build/GocciaScriptLoader example.js --allowed-host=api.example.com --allowed-host=cdn.example.com # Execute with fetch restricted to multiple hosts
./build/GocciaScriptLoader example.js --log=console.log # Write console output to a log file (tee to stdout + file)
./build/GocciaScriptLoader example.js --stack-size=5000 # Execute with custom call stack depth limit
./build/GocciaScriptLoader example.js --stack-size=0 --mode=bytecode # Execute with no call stack depth limit (bytecode trampoline)
./build/GocciaScriptLoader example.js --inspect-depth=2 # Execute with shallow object inspection (default: 5)
./build/GocciaScriptLoader example.js --multifile # Split file on '---' separators and run each section as its own input
./build/GocciaScriptLoader example.js --multifile --jobs=4 # Multifile with parallel section dispatch
printf '1;\n---\n2;\n' | ./build/GocciaScriptLoader --multifile # Split stdin on '---' (sections named <stdin>[part1], <stdin>[part2])
./build/GocciaREPL # Start interactive REPL (interpreted)
./build/GocciaREPL --mode=bytecode # Start the REPL via bytecode VM
./build/GocciaREPL --mode=bytecode --timing # Bytecode REPL with per-line timing
./build/GocciaREPL --import-map=imports.json # Start the REPL with an explicit import map
./build/GocciaREPL --config=./configs/dev.json # Start the REPL with an explicit config path (skips auto-discovery)
./build/GocciaREPL --asi # Start the REPL with automatic semicolon insertion
./build/GocciaREPL --unsafe-ffi # Start the REPL with FFI enabled
./build/GocciaREPL --log=repl.log # Start the REPL with console log capture
./build/GocciaREPL --stack-size=5000 # Start the REPL with custom call stack depth limit
./build/GocciaREPL --max-memory=10485760 # Start the REPL with 10 MB GC heap limit
./build/GocciaTestRunner tests/ --asi # Run all JavaScript tests
./build/GocciaTestRunner tests --import-map=imports.json # Run tests with an explicit import map
./build/GocciaTestRunner tests --config=./configs/ci.json # Run tests with an explicit config path (skips auto-discovery)
./build/GocciaTestRunner tests/language/expressions/ # Run a test category
./build/GocciaTestRunner tests --no-progress --exit-on-first-failure # CI mode
./build/GocciaTestRunner tests --silent # Suppress all console output
./build/GocciaTestRunner tests --output=results.json # Write test results as JSON to a file
./build/GocciaTestRunner tests --output=json # Emit a structured JSON envelope to stdout
./build/GocciaTestRunner tests --output=compact-json # Same envelope to stdout, omitting build, memory, stdout, and stderr
./build/GocciaTestRunner tests --mode=bytecode # Run tests via the Goccia bytecode VM
./build/GocciaTestRunner tests/language/asi --asi # Run ASI tests with automatic semicolon insertion
./build/GocciaTestRunner tests --coverage # Run tests with line and branch coverage
./build/GocciaTestRunner tests --coverage --coverage-format=lcov --coverage-output=coverage.lcov # Coverage with lcov output
./build/GocciaTestRunner tests --coverage --coverage-format=json --coverage-output=coverage.json # Coverage with JSON output
./build/GocciaTestRunner tests --jobs=4 # Run tests with 4 parallel workers
./build/GocciaTestRunner tests -j 1 # Force sequential execution (no threading)
./build/GocciaTestRunner tests --asi --unsafe-ffi # Run all tests including FFI tests
./build/GocciaTestRunner tests --log=test-console.log # Capture console output to a log file
./build/GocciaTestRunner suites.js --multifile # Split a single test file on '---' and run each section as its own file
printf 'test("two plus two", () => { expect(2 + 2).toBe(4); });\n' | ./build/GocciaTestRunner # Run a test source from stdin
./build/GocciaBenchmarkRunner benchmarks/ # Run all benchmarks
./build/GocciaBenchmarkRunner benchmarks --import-map=imports.json # Run benchmarks with an explicit import map
./build/GocciaBenchmarkRunner benchmarks --config=./configs/bench.toml # Run benchmarks with an explicit config path (skips auto-discovery)
./build/GocciaBenchmarkRunner benchmarks/fibonacci.js # Run a specific benchmark
printf 'suite("stdin", () => { bench("sum", { run: () => 1 + 1 }); });\n' | ./build/GocciaBenchmarkRunner # Run benchmark source from stdin
./build/GocciaBenchmarkRunner benchmarks --format=json --output=out.json # Export as JSON
./build/GocciaBenchmarkRunner benchmarks --format=compact-json --output=out.json # Export as JSON, omitting build, memory, stdout, and stderr
./build/GocciaBenchmarkRunner benchmarks --format=console --format=json --output=out.json # Console + JSON
./build/GocciaBenchmarkRunner benchmarks --no-progress # Suppress progress (CI)
./build/GocciaBenchmarkRunner benchmarks --mode=bytecode # Benchmarks via the Goccia bytecode VM
./build/GocciaBenchmarkRunner benchmarks --jobs=4 # Run benchmarks with 4 parallel workers
./build/GocciaBenchmarkRunner suites.js --multifile # Split a single benchmark file on '---' and run each section as its own file
./build/GocciaBundler example.js # Compile to .gbc (no execution)
./build/GocciaBundler example.js --output=out.gbc # Custom output path
./build/GocciaBundler src/ # Compile all scripts in a directory
./build/GocciaBundler src/ --output=dist/ # Compile directory to output directory
./build/GocciaBundler example.js --source-map # Write .map source map alongside .gbc
printf "const x = 2 + 2; x;" | ./build/GocciaBundler --output=out.gbc # Compile stdin to .gbc
./build/GocciaBundler src/ --jobs=4 # Compile with 4 parallel workers
./build/GocciaBundler example.js --asi # Compile with automatic semicolon insertion
./build/GocciaBundler example.js --multifile --output=dist/ # Split on '---' and emit one .gbc per section into dist/
./build/GocciaBundler example.js --config=./configs/release.toml # Compile with an explicit config path (skips auto-discovery)
```

### Compile and run (common workflows)

```bash
# Compile and run a script
./build.pas loader && ./build/GocciaScriptLoader ./example.js

# Compile and run all tests
./build.pas testrunner && ./build/GocciaTestRunner tests --asi --unsafe-ffi

# Compile and run a specific test
./build.pas testrunner && ./build/GocciaTestRunner tests/language/expressions/addition/basic-addition.js

# Check formatting without modifying files
./format.pas --check

# Auto-format all Pascal files
./format.pas
```

### Configuration file

All CLI options can be set via `goccia.json` (or `.json5` / `.toml`) discovered from the entry file's directory upward. Priority: TOML > JSON5 > JSON. CLI arguments override config values. Config files support `"extends"` to inherit from a base config. Pass `--config=<path>` to skip auto-discovery — the path may be a config file or a directory (which is searched only for `goccia.{toml,json5,json}` without walking up).

```json
// goccia.json
{ "asi": true, "mode": "bytecode", "timeout": 5000, "allowed-hosts": ["api.example.com"] }
```

```json
// tests/language/asi/goccia.json — enables ASI for this subtree
{ "asi": true }
```

```json
// subdirectory config inheriting from parent
{ "extends": "../../goccia.json", "mode": "bytecode" }
```

See [Configuration File](docs/build-system.md#configuration-file-gocciajson) for full reference.

## Where to go next

- **Contribution requirements:** [CONTRIBUTING.md](CONTRIBUTING.md)
- **Engine shape:** [docs/architecture.md](docs/architecture.md), [docs/interpreter.md](docs/interpreter.md), [docs/bytecode-vm.md](docs/bytecode-vm.md), [docs/core-patterns.md](docs/core-patterns.md)
- **Optional extended agent skills:** [.agents/skills/](.agents/skills/) (installable playbooks; not a substitute for CONTRIBUTING)
