Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Nextest configuration for submod
#
# With per-test git config isolation (GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM),
# tests are safe to run in parallel. The serial-integration group is kept as
# defense-in-depth: integration tests that create repos, add/remove submodules,
# and shell out to git are serialized among themselves to avoid any residual
# contention on shared git state (e.g. lock files in /tmp).

[test-groups]
serial-integration = { max-threads = 1 }

[profile.default]
fail-fast = false

# Integration tests that create/modify git repos run serially within their
# group. config_tests and command_contract_tests are safe to run in parallel
# with everything (they use isolated temp dirs and per-test gitconfig).
[[profile.default.overrides]]
filter = 'binary_id(submod::integration_tests) | binary_id(submod::sparse_checkout_tests) | binary_id(submod::error_handling_tests) | binary_id(submod::performance_tests)'
test-group = 'serial-integration'

# CI profile: fail fast but still serialize integration tests
[profile.ci]
fail-fast = true

[[profile.ci.overrides]]
filter = 'binary_id(submod::integration_tests) | binary_id(submod::sparse_checkout_tests) | binary_id(submod::error_handling_tests) | binary_id(submod::performance_tests)'
test-group = 'serial-integration'
2 changes: 2 additions & 0 deletions .serena/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/cache
/project.local.yml
138 changes: 138 additions & 0 deletions .serena/project.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# the name by which the project can be referenced within Serena
project_name: "submod"


# list of languages for which language servers are started; choose from:
# al bash clojure cpp csharp
# csharp_omnisharp dart elixir elm erlang
# fortran fsharp go groovy haskell
# java julia kotlin lua markdown
# matlab nix pascal perl php
# php_phpactor powershell python python_jedi r
# rego ruby ruby_solargraph rust scala
# swift terraform toml typescript typescript_vts
# vue yaml zig
# (This list may be outdated. For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
# Note:
# - For C, use cpp
# - For JavaScript, use typescript
# - For Free Pascal/Lazarus, use pascal
# Special requirements:
# Some languages require additional setup/installations.
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
# When using multiple languages, the first language server that supports a given file will be used for that file.
# The first language is the default language and the respective language server will be used as a fallback.
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
languages:
- rust

# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"

# line ending convention to use when writing source files.
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
line_ending:

# The language backend to use for this project.
# If not set, the global setting from serena_config.yml is used.
# Valid values: LSP, JetBrains
# Note: the backend is fixed at startup. If a project with a different backend
# is activated post-init, an error will be returned.
language_backend:

# whether to use project's .gitignore files to ignore files
ignore_all_files_in_gitignore: true

# list of additional paths to ignore in this project.
# Same syntax as gitignore, so you can use * and **.
# Note: global ignored_paths from serena_config.yml are also applied additively.
ignored_paths: []

# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false

# list of tool names to exclude.
# This extends the existing exclusions (e.g. from the global configuration)
#
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
#
# * `activate_project`: Activates a project by name.
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
# * `create_text_file`: Creates/overwrites a file in the project directory.
# * `delete_lines`: Deletes a range of lines within a file.
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
# * `execute_shell_command`: Executes a shell command.
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
# * `initial_instructions`: Gets the initial instructions for the current project.
# Should only be used in settings where the system prompt cannot be set,
# e.g. in clients you have no control over, like Claude Desktop.
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
# * `insert_at_line`: Inserts content at a given line in a file.
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
# * `list_memories`: Lists memories in Serena's project-specific memory store.
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
# * `read_file`: Reads a file within the project directory.
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
# * `remove_project`: Removes a project from the Serena configuration.
# * `replace_lines`: Replaces a range of lines within a file with new content.
# * `replace_symbol_body`: Replaces the full definition of a symbol.
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
# * `search_for_pattern`: Performs a search for a pattern in the project.
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
# * `switch_modes`: Activates modes by providing a list of their names
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
excluded_tools: []

# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
# This extends the existing inclusions (e.g. from the global configuration).
included_optional_tools: []

# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
fixed_tools: []

# list of mode names to that are always to be included in the set of active modes
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this setting overrides the global configuration.
# Set this to [] to disable base modes for this project.
# Set this to a list of mode names to always include the respective modes for this project.
base_modes:

# list of mode names that are to be activated by default.
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# This setting can, in turn, be overridden by CLI parameters (--mode).
default_modes:

# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""

# time budget (seconds) per tool call for the retrieval of additional symbol information
# such as docstrings or parameter information.
# This overrides the corresponding setting in the global configuration; see the documentation there.
# If null or missing, use the setting from the global configuration.
symbol_info_budget:

# list of regex patterns which, when matched, mark a memory entry as read‑only.
# Extends the list from the global configuration, merging the two lists.
read_only_memory_patterns: []
165 changes: 74 additions & 91 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,110 +1,93 @@
<!--
SPDX-FileCopyrightText: 2025 Adam Poulemanos <89049923+bashandbone@users.noreply.github.com>

SPDX-License-Identifier: LicenseRef-PlainMIT OR MIT
-->

# CLAUDE.md

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

## Project Overview

`submod` is a Rust CLI tool for managing git submodules with advanced sparse checkout support. It uses `gitoxide` and `git2` libraries for high-performance git operations, with fallbacks to CLI git commands when needed.

## Development Commands

### Building and Testing

- `cargo build` - Build the project
- `cargo test` - Run unit tests
- `./scripts/run-tests.sh` - Run comprehensive integration test suite
- `./scripts/run-tests.sh --verbose` - Run tests with detailed output
- `./scripts/run-tests.sh --performance` - Include performance tests
- `./scripts/run-tests.sh --filter <pattern>` - Run specific test modules

### Linting and Formatting

- `cargo fmt` - Format code
- `cargo clippy` - Run linter
- `hk run check` - Run pre-commit checks via hk tool
- `hk run fix` - Run auto-fixable linters

### Mise Tasks (if using mise)

- `mise run build` or `mise run b` - Build the CLI
- `mise run test` - Run automated tests
- `mise run lint` - Lint with clippy
- `mise run ci` - Run CI tasks (build, lint, test)

### Testing Philosophy

The project follows integration-first testing. Focus on testing complete workflows and outputs rather than implementation details. Tests are run serially (`RUST_TEST_THREADS=1`) to avoid git submodule race conditions.
## Commands

### Build & Run
```bash
cargo build # debug build
cargo build --release # release build
mise run build # alias: mise run b
```

### Test
```bash
cargo nextest run --all-features --no-fail-fast # run all tests (preferred)
cargo test --test integration_tests # integration tests only
cargo test --test command_contract_tests # command contract tests only
./scripts/run-tests.sh --verbose # comprehensive test runner with reporting
./scripts/run-tests.sh --filter sparse_checkout # filter to specific tests
mise run test # alias: mise run t (runs via hk)
```

Integration tests that modify git repos are serialized via nextest test groups (`.config/nextest.toml`); other tests run in parallel. Each test gets an isolated git config via `GIT_CONFIG_GLOBAL` so tests never race on `~/.gitconfig`. The test suite is integration-test-focused; unit tests are minimal by design.

### Lint & Format
```bash
cargo fmt # format code
cargo clippy --all-features # lint
hk run check # run all linters (fmt, clippy, deny, typos, pkl)
hk fix # auto-fix where possible
mise run lint # alias for hk run check
```

### Full CI
```bash
mise run ci # build + lint + test
hk run ci # same via hk (uses --fail-fast for tests)
```

### Git Hooks
Managed by `hk` (configured in `hk.pkl`). Pre-commit runs: cargo fmt, clippy, check, nextest, typos, cargo-deny, pkl eval. Hooks install automatically with `mise install`.

## Architecture

### Core Modules

- `src/main.rs` - CLI entry point, parses commands and dispatches to manager
- `src/commands.rs` - Command-line argument definitions using clap
- `src/config.rs` - TOML configuration parsing and submodule config management
- `src/git_manager.rs` - Core submodule operations using gitoxide/git2
- `src/lib.rs` - Library exports (not a stable API)

### Configuration System

- Uses TOML configuration files (default: `submod.toml`)
- Supports global defaults with per-submodule overrides
- Handles sparse checkout paths, git options, and submodule settings

### Git Operations Strategy

1. **Primary**: gitoxide library for performance
2. **Fallback**: git2 library (optional feature `git2-support`)
3. **Final fallback**: CLI git commands

### Key Design Patterns

- Error handling with `anyhow` for application errors, `thiserror` for library errors
- Comprehensive documentation for all public APIs
- Strict linting configuration with pedantic clippy settings
- Integration tests over unit tests
`submod` is a CLI tool for managing git submodules with TOML configuration and sparse checkout support. It exposes the library as `src/lib.rs` primarily for integration testing (the public API is not stable).

## Configuration Files
### Layer Stack

### Key Files to Know
```
CLI (commands.rs + main.rs)
↓ clap parsing
GitManager (git_manager.rs) ← high-level submodule operations
↓ delegates to
GitOpsManager (git_ops/mod.rs) ← unified backend with automatic fallback
├── GixOperations (git_ops/gix_ops.rs) ← gitoxide (preferred)
├── Git2Operations (git_ops/git2_ops.rs) ← libgit2 (fallback)
└── Git CLI ← last resort (spawned via std::process)
Config (config.rs) ← figment-based TOML config loading/saving
```

- `Cargo.toml` - Project configuration with strict linting rules
- `hk.pkl` - Git hooks configuration (pre-commit, linting)
- `mise.toml` - Development environment and task definitions
- `sample_config/submod.toml` - Example configuration
- `scripts/run-tests.sh` - Comprehensive test runner
### Fallback Architecture

### Test Structure
The core design is a **gix-first, git2-fallback, CLI-last-resort** strategy, driven by the immaturity of gitoxide's submodule support. `GitOpsManager` wraps both backends behind the `GitOperations` trait and calls `try_with_fallback()` / `try_with_fallback_mut()` for every operation. When gix fails, it logs a warning and transparently retries with git2; `add_submodule` has an additional CLI fallback that also cleans up any partial state from the prior attempt.

- `tests/integration_tests.rs` - Core functionality tests
- `tests/config_tests.rs` - Configuration parsing tests
- `tests/sparse_checkout_tests.rs` - Sparse checkout functionality
- `tests/error_handling_tests.rs` - Error conditions and edge cases
- `tests/performance_tests.rs` - Performance and stress tests
After destructive operations (delete, nuke), `GitOpsManager::reopen()` must be called to refresh the in-memory repository state. git2 reopen errors are fatal; gix reopen errors are warnings only.

## Working with the Codebase
### Configuration

### Before Making Changes
`Config` uses [figment](https://docs.rs/figment) to load `submod.toml`. The schema has:
- `[defaults]` → `SubmoduleDefaults` (global git options applied to all submodules)
- `[<name>]` sections → `SubmoduleEntry` (per-submodule config, overrides defaults)

1. Run `./scripts/run-tests.sh` to ensure tests pass
2. Check code formatting with `cargo fmt --check`
3. Run linter with `cargo clippy`
`SubmoduleEntry` merges `SubmoduleGitOptions` (ignore, fetch_recurse, branch, update) with submodule-specific fields (path, url, sparse_paths, active, shallow). Options are serialized to/from git-config-compatible strings via the `options.rs` newtype wrappers (`SerializableIgnore`, `SerializableUpdate`, etc.).

### When Adding Features
### Key Conventions

- Add integration tests to appropriate test modules
- Update configuration parsing if new config options are added
- Follow existing error handling patterns
- Document public APIs thoroughly
- **Unsafe code is forbidden** (`unsafe_code = "forbid"` in `Cargo.toml`)
- Clippy is configured at `pedantic` + `nursery` warn level; `correctness` is deny
- `missing_docs` is warn — public items need doc comments
- `module_name_repetitions` and `too_many_lines` are allowed
- All error handling uses `anyhow` for propagation and `thiserror` for defining error types
- `simple_gix.rs` contains lightweight gix helpers used in `gix_ops.rs`

### Code Quality Standards
### Testing Approach

- Unsafe code is forbidden (`unsafe_code = "forbid"`)
- All warnings treated seriously with comprehensive clippy configuration
- Focus on clear, descriptive naming and comprehensive error messages
Integration tests in `tests/` use a `TestHarness` (in `tests/common/mod.rs`) that creates temporary git repos and invokes the compiled binary. Test files:
- `integration_tests.rs` — end-to-end CLI behavior
- `command_contract_tests.rs` — CLI command argument contracts
- `config_tests.rs` — configuration parsing/serialization
- `sparse_checkout_tests.rs` — sparse checkout behavior
- `error_handling_tests.rs` — error conditions and messages
- `performance_tests.rs` — benchmarks (uses criterion)
Loading
Loading