| 1 | # Contributing to Reasonix |
| 2 | |
| 3 | Thank you for your interest in contributing to Reasonix! This guide covers |
| 4 | everything you need to get started. |
| 5 | |
| 6 | ## Prerequisites |
| 7 | |
| 8 | - **Go 1.25+** — the project targets the latest stable Go release |
| 9 | - **Git** — for version control |
| 10 | - **Node.js** (optional) — only if you work on the desktop app (`desktop/`) |
| 11 | |
| 12 | ## Getting started |
| 13 | |
| 14 | ```bash |
| 15 | git clone https://github.com/esengine/DeepSeek-Reasonix.git |
| 16 | cd DeepSeek-Reasonix |
| 17 | go build ./cmd/reasonix # builds the CLI binary |
| 18 | go test ./... # runs the full test suite |
| 19 | ``` |
| 20 | |
| 21 | ## Project structure |
| 22 | |
| 23 | | Directory | Purpose | |
| 24 | |-----------|---------| |
| 25 | | `cmd/reasonix` | CLI entry point | |
| 26 | | `internal/agent` | Agent loop, session, coordinator | |
| 27 | | `internal/cli` | TUI, subcommands, setup wizard | |
| 28 | | `internal/control` | Transport-agnostic controller | |
| 29 | | `internal/config` | TOML configuration loading | |
| 30 | | `internal/tool/builtin` | Built-in tools (bash, read_file, …) | |
| 31 | | `internal/provider` | Model-backend abstraction | |
| 32 | | `internal/provider/openai` | OpenAI-compatible provider | |
| 33 | | `internal/plugin` | MCP client (stdio + HTTP) | |
| 34 | | `internal/event` | Typed event stream | |
| 35 | | `internal/hook` | Shell hooks (PreToolUse, …) | |
| 36 | | `internal/memory` | REASONIX.md hierarchy + auto-memory | |
| 37 | | `internal/skill` | Skill discovery from Markdown | |
| 38 | | `internal/sandbox` | OS-level sandboxing | |
| 39 | | `internal/serve` | HTTP/SSE server frontend | |
| 40 | | `internal/checkpoint` | Snapshot-based rewind | |
| 41 | | `desktop/` | Wails-based desktop app (separate Go module) | |
| 42 | | `docs/` | Engineering spec, migration guide | |
| 43 | |
| 44 | ### Dependency direction |
| 45 | |
| 46 | ``` |
| 47 | cli → {agent, plugin, config} → {tool, provider} |
| 48 | ``` |
| 49 | |
| 50 | Built-in subpackages import their parent to self-register via `init()`. |
| 51 | Parents never import children. |
| 52 | |
| 53 | ## Development workflow |
| 54 | |
| 55 | ### Building |
| 56 | |
| 57 | ```bash |
| 58 | make build # go build ./... |
| 59 | make test # go test ./... |
| 60 | make vet # go vet ./... |
| 61 | make fmt # gofmt -w . |
| 62 | make hooks # install git hooks (pre-push: go vet) |
| 63 | make cross # cross-compile for all 6 targets |
| 64 | ``` |
| 65 | |
| 66 | ### Isolated development environment |
| 67 | |
| 68 | A source-built binary shares no on-disk state with a stable release when launched |
| 69 | with `REASONIX_HOME` set. This gives each build its own self-contained directory |
| 70 | tree — config, credentials, sessions, cache, skills, commands, hooks, and |
| 71 | desktop tab state — so the two builds never interfere: |
| 72 | |
| 73 | **CLI** |
| 74 | |
| 75 | ```bash |
| 76 | REASONIX_HOME=/tmp/reasonix-dev go run ./cmd/reasonix |
| 77 | # or after building: |
| 78 | # REASONIX_HOME=/tmp/reasonix-dev ./bin/reasonix |
| 79 | ``` |
| 80 | |
| 81 | **Desktop** |
| 82 | |
| 83 | ```bash |
| 84 | cd desktop && wails build |
| 85 | REASONIX_HOME=/tmp/reasonix-dev-isolated build/bin/reasonix-desktop |
| 86 | ``` |
| 87 | |
| 88 | On Windows, use `$env:REASONIX_HOME` in PowerShell or `set REASONIX_HOME=` in |
| 89 | Command Prompt; the binary extension is `.exe`. |
| 90 | |
| 91 | The directory is empty on first launch; the app behaves exactly like a fresh |
| 92 | install. Every subsequent write — config saves, credential storage, session |
| 93 | logs — stays under `REASONIX_HOME`. Legacy migration, OS-home convention |
| 94 | directory scanning, and all other fallback paths are skipped so no production |
| 95 | data leaks in or out. |
| 96 | |
| 97 | ### Cache-first review gate |
| 98 | |
| 99 | Reasonix treats high prompt-cache hit rate as product behavior. Changes that |
| 100 | touch provider-visible system prompt construction, memory prefix, output styles, |
| 101 | skill index behavior, default tool surfaces, tool schemas, provider request |
| 102 | serialization, compaction, or MCP/tool registration need explicit cache review. |
| 103 | |
| 104 | For these changes: |
| 105 | |
| 106 | - Keep system prompt changes low-frequency and require explicit review. |
| 107 | - Fill the PR body `Cache-impact:` line with `none`, `low`, `medium`, or `high` |
| 108 | plus the reason. |
| 109 | - Fill the PR body `Cache-guard:` line with the focused guard test/command added |
| 110 | or run, or explain why an existing guard covers the change. |
| 111 | - Fill `System-prompt-review:` when system prompt, memory prefix, output style, |
| 112 | or skill index behavior changes. |
| 113 | - Prefer focused guard tests near the changed surface; `scripts/cache-guard.sh` |
| 114 | remains the broader release-level cache-hit check. |
| 115 | |
| 116 | CI enforces this metadata for cache-sensitive paths so prompt/tool prefix churn |
| 117 | is called out before review. |
| 118 | |
| 119 | ### Running tests |
| 120 | |
| 121 | ```bash |
| 122 | go test ./... # all tests |
| 123 | go test ./internal/agent/ -v # verbose, one package |
| 124 | go test ./internal/tool/builtin/ -run TestGrep # one test |
| 125 | ``` |
| 126 | |
| 127 | ### Code style |
| 128 | |
| 129 | - `gofmt` is enforced by CI — format before committing |
| 130 | - Follow existing patterns: wrap errors with `fmt.Errorf("...: %w", err)` |
| 131 | - Library code never calls `os.Exit` or prints to stdout/stderr |
| 132 | - Only `cli/` and `main/` decide exit codes and user-facing messages |
| 133 | - Exported identifiers must have doc comments |
| 134 | |
| 135 | ### Commit messages |
| 136 | |
| 137 | Follow [Conventional Commits](https://www.conventionalcommits.org/): |
| 138 | |
| 139 | ``` |
| 140 | feat(glob): add ** recursive pattern support |
| 141 | fix: replace silent error discards with structured logging |
| 142 | test(event): add comprehensive unit tests for event package |
| 143 | docs: add CONTRIBUTING.md |
| 144 | ci: add golangci-lint and govulncheck |
| 145 | ``` |
| 146 | |
| 147 | ## Adding a new built-in tool |
| 148 | |
| 149 | 1. Create `internal/tool/builtin/mytool.go` |
| 150 | 2. Implement the `tool.Tool` interface: `Name()`, `Description()`, `Schema()`, `ReadOnly()`, `Execute()` |
| 151 | 3. Register via `func init() { tool.RegisterBuiltin(myTool{}) }` |
| 152 | 4. Add tests in `internal/tool/builtin/builtin_test.go` or a separate `mytool_test.go` |
| 153 | 5. The tool is automatically available — `main` blank-imports `builtin` |
| 154 | |
| 155 | ## Adding a new model provider |
| 156 | |
| 157 | (For MCP tool servers see `internal/plugin` instead — that's a different layer.) |
| 158 | |
| 159 | 1. Create `internal/provider/myprovider/` |
| 160 | 2. Implement `provider.Provider`: `Name()`, `Stream()` |
| 161 | 3. Register via `func init() { provider.Register("mykind", New) }` |
| 162 | 4. The provider is available from config with `kind = "mykind"` |
| 163 | |
| 164 | ## Adding i18n strings |
| 165 | |
| 166 | 1. Add the field to `internal/i18n/i18n.go` (`Messages` struct) |
| 167 | 2. Add the value in `internal/i18n/messages_en.go` and `messages_zh.go` |
| 168 | 3. The `TestCatalogsComplete` test will fail if you miss a locale |
| 169 | |
| 170 | ## Submitting changes |
| 171 | |
| 172 | 1. Fork the repository |
| 173 | 2. Create a feature branch from `main-v2` |
| 174 | 3. Make your changes with tests |
| 175 | 4. Ensure `go test ./...` passes |
| 176 | 5. Ensure `gofmt -l .` shows no changes |
| 177 | 6. Submit a pull request to `main-v2` |
| 178 | |
| 179 | ## Reporting issues |
| 180 | |
| 181 | Open an issue on GitHub with: |
| 182 | - Steps to reproduce |
| 183 | - Expected vs actual behavior |
| 184 | - Go version and OS |
| 185 | - Relevant logs or error messages |
| 186 | |
| 187 | ## License |
| 188 | |
| 189 | By contributing, you agree that your contributions will be licensed under the |
| 190 | same license as the project. |
| 191 |