Contributing¶
Prerequisites¶
- Go 1.25 or later
- golangci-lint v2+
Getting started¶
That runs formatting, vetting, linting, and tests with race detection. If it passes, you're set.
Building¶
# Build the binary
make build
# Run without building
go run ./cmd/sctx version
# Install to $GOPATH/bin
go install ./cmd/sctx
Running tests¶
# All tests with race detection
make test
# Specific package
go test ./internal/core/...
# Specific test
go test ./internal/core/... -run TestResolve_EditBefore
# With coverage
make cover
Project structure¶
cmd/sctx/ CLI entry point. Thin dispatch layer.
internal/
core/ Agent-agnostic engine. Discovers, parses,
filters, and merges context files.
adapter/ Agent-specific translation layers. Each
adapter maps agent input to a ResolveRequest.
validator/ Schema validation for context files.
docs/ Documentation (you're reading it).
The key boundary: internal/core must never import from internal/adapter. Agent-specific logic stays in adapters.
Making changes¶
- Create a branch off
main - Make your changes
- Run
make check-- fmt, vet, lint, and tests must all pass - Open a PR
Adding a new adapter¶
Create a new file in internal/adapter/ (e.g., cursor.go). Your adapter reads whatever input the agent provides, maps it to a core.ResolveRequest, calls core.Resolve, and formats the output. Look at claude.go for the pattern.
Adding new context fields¶
- Add the field to the struct in
internal/core/schema.go - Set a default in
applyDefaultsinengine.goif needed - If the field acts as a filter, add filtering logic in
filterContext()orfilterDecisions()inengine.go - Add validation in
internal/validator/validate.go - Update testdata fixtures
- Update
docs/protocol.md
Test conventions¶
- Table-driven tests for unit logic
- Test fixtures go in
testdata/directories - Use
t.TempDir()when the test needs dynamic file creation - No assertion libraries -- plain
if+t.Errorf - Test names follow
TestFunctionName_Scenario
Linting¶
We use golangci-lint with a tuned config in .golangci.yml. Some linters are intentionally disabled with rationale in the config file. If you think the linter found a false positive, check the config before adding a //nolint directive. If you do add one, include the linter name and a reason:
Validating context files¶
The project uses its own AGENTS.yaml files. After editing them: