ngit 2.6.2

nostr plugin for git
Documentation
# AGENTS.md

Documentation for AI agents and automated tools working with the ngit codebase.

## Project Overview

**ngit** is a nostr plugin for git that enables decentralized code collaboration using the nostr protocol.

- **Language**: Rust
- **Type**: Command-line tool with git integration
- **Architecture**: Two main binaries (`ngit` and `git-remote-nostr`) with shared library code
- **Key Dependencies**: nostr-sdk, git2, clap, tokio

### Core Concepts

1. **Nostr Integration**: Uses nostr for repository identification, discovery, state management, and collaboration (PRs, issues)
2. **Git Server Agnostic**: Still requires git servers for data storage, but they're interchangeable
3. **Decentralized**: Multiple relays and git servers can be used for redundancy
4. **URL Format**: `nostr://<npub|nip05-address>/<identifier>`

## Project Structure

```
ngit/
├── src/
│   ├── bin/
│   │   ├── git_remote_nostr/    # Git remote helper implementation
│   │   │   ├── capabilities.rs
│   │   │   ├── fetch.rs
│   │   │   ├── list.rs
│   │   │   ├── main.rs
│   │   │   └── push.rs
│   │   └── ngit/                # Main CLI tool
│   │       ├── main.rs
│   │       └── sub_commands/
│   └── lib/                     # Shared library code
│       ├── git/                 # Git operations
│       ├── login/               # User authentication
│       ├── client.rs            # Nostr client
│       ├── fetch.rs             # Fetch operations
│       ├── git_events.rs        # Git event handling
│       ├── list.rs              # List operations
│       ├── push.rs              # Push operations
│       ├── repo_ref.rs          # Repository references
│       ├── repo_state.rs        # Repository state
│       └── utils.rs             # Utilities
├── tests/                       # Integration tests
├── test_harness/                # e2e test utility
└── git_hooks/                   # Git hooks for development
```

## Key Files and Their Purposes

### Core Library (`src/lib/`)

- **`repo_ref.rs`**: Repository reference handling, URL parsing, grasp server detection
  - Functions: `is_grasp_server_in_list()`, `is_grasp_server_clone_url()`, `normalize_grasp_server_url()`
  - Critical for identifying and validating repository URLs
- **`repo_state.rs`**: Manages repository state (refs, commits, etc.)

- **`client.rs`**: Nostr client wrapper and relay communication

- **`git_events.rs`**: Handles git-related nostr events

- **`push.rs`**: Push operations to git servers and nostr relays

- **`fetch.rs`**: Fetch operations from git servers

### Binaries

- **`git-remote-nostr`**: Git remote helper that enables git to work with nostr:// URLs
- **`ngit`**: Main CLI tool for managing nostr repositories

## Development Guidelines

### Code Style

- Follow Rust standard formatting (`rustfmt.toml` is configured)
- Run `cargo fmt` before committing
- Run `cargo clippy` to catch common issues
- Use `anyhow::Result` for error handling
- Prefer explicit error messages with context

### Testing

```bash
# Run all tests
cargo test

# Run specific test
cargo test test_name

# Run tests with output
cargo test -- --nocapture

# Run integration tests only
cargo test --test ngit_init
```

### Important Testing Notes

1. **URL Comparison**: When comparing git server URLs, be aware of normalization:
   - `is_grasp_server_in_list()`: Compares full URLs (including repo path)
   - `normalize_grasp_server_url()`: Extracts server part only (strips npub and path)

2. **Test Structure**: Integration tests are in `tests/`, unit tests are in module files

### Building

```bash
# Development build
cargo build

# Release build
cargo build --release

# Binaries will be in target/debug/ or target/release/
# - ngit
# - git-remote-nostr
```

## Common Tasks for Agents

### Adding New Features

1. **Identify the correct module**:
   - Git operations → `src/lib/git/`
   - Nostr operations → `src/lib/client.rs`, `src/lib/git_events.rs`
   - CLI commands → `src/bin/ngit/sub_commands/`
   - Remote helper → `src/bin/git_remote_nostr/`

2. **Add tests**: Always add tests for new functionality

3. **Update error handling**: Use `anyhow::Context` for error messages

4. **Check dependencies**: Avoid adding unnecessary dependencies

### Debugging Issues

1. **Check test failures**: Run `cargo test` to identify failing tests

2. **Review error context**: Look at the full error chain with `.context()`

3. **Examine URL handling**: Many issues relate to URL parsing/normalization
   - Check `repo_ref.rs` for URL-related functions
   - Verify grasp server detection logic

4. **Trace nostr events**: Check event creation and publishing in `git_events.rs`

### Refactoring

1. **Maintain backward compatibility**: This is a CLI tool users depend on

2. **Update all call sites**: Use `cargo check` to find all references

3. **Run full test suite**: Ensure nothing breaks

4. **Check integration tests**: These test real-world scenarios

## Architecture Patterns

### Error Handling

```rust
use anyhow::{Context, Result};

fn example() -> Result<()> {
    something()
        .context("descriptive error message")?;
    Ok(())
}
```

### Async Operations

- Uses `tokio` runtime for async operations
- Nostr operations are async
- Git operations are mostly sync

### Configuration

- User config stored in platform-specific directories (via `directories` crate)
- Repository config in `.git/config` and `ngit.yaml`

## Important Invariants

### URL Handling

1. **Grasp Server URLs**: Must contain npub in path format: `/{npub}/`
2. **Nostr URLs**: Format: `nostr://<npub|nip05>/<identifier>`
3. **Normalization**: Different functions normalize differently:
   - Full URL comparison: Use direct string comparison with `trim_end_matches('/')`
   - Server identification: Use `normalize_grasp_server_url()`

### Git Integration

1. **Remote helper protocol**: Must follow git's remote helper protocol exactly
2. **Ref naming**: PRs use `pr/` prefix
3. **State sync**: Must keep git refs in sync with nostr events

### Tag round-tripping (kind:30617 announcements)

`RepoRef::try_from` preserves any tag whose first slot is not in
`is_known_tag_name` into `RepoRef::extra_tags`. `RepoRef::to_event`
re-emits those tags verbatim after the typed-field block. The intent
is that ngit never silently strips a tag added by a future ngit
version or a third-party tool.

When adding a new typed tag to ngit's emission:

1. Add a parse arm in `RepoRef::try_from` (`src/lib/repo_ref.rs`).
2. Add the name to `is_known_tag_name` so duplicates on the source
   event don't smuggle past the typed field and produce a double
   emission. The `extra_tags_round_trip::allowlist_matches_emitted_tag_names`
   unit test catches this drift.
3. Decide whether the field should cascade from the latest event
   across maintainers (like `name`/`description`/`web`/`hashtags`/
   `extra_tags`) by sourcing it from `latest_event_repo_ref(rr)` in
   `init.rs::resolve_fields`.

`ngit init --clean` drops the inherited `extra_tags` on republish.

## Dependencies to Be Aware Of

### Critical Dependencies

- **nostr**: Core nostr protocol implementation
- **git2**: Git operations via libgit2
- **tokio**: Async runtime
- **anyhow**: Error handling

### Optional Features

Check `Cargo.toml` for feature flags and optional dependencies.

## Testing Strategy

### Unit Tests

- Located in same file as code (`#[cfg(test)] mod tests`)
- Test individual functions in isolation
- Example: `is_grasp_server_in_list` tests in `repo_ref.rs`

### Integration Tests

- Located in `tests/` directory
- Test full workflows (init, push, fetch, etc.)
- All integration tests use `test_harness/`. See
  `docs/architecture/test-harness.md` for design notes; the rules in
  "Test harness boundary" below are non-negotiable.

### Test Utilities

- `test_harness/` — integration test crate. Use this for all new
  integration tests.

### Test harness boundary (mandatory rules for new tests)

These rules exist because the previous harness collapsed under
exactly these patterns. They are non-negotiable:
- **No `#[serial]` annotations.** Test isolation is provided by the
  harness (per-test temp dirs, per-test ports, per-test env). If a
  test needs serialisation, the harness contract is wrong; fix the
  harness.
- **No PTY / `rexpect` / `dialoguer` interaction.** Drive ngit
  through flags only. Tests that require an interactive prompt are
  out of scope; cover the underlying logic with a unit test instead.
- **No `std::env::set_var` from a test or helper.** Spawned children
  receive env via `Repo::ngit` / `Repo::git`, which scope vars to the
  subprocess. Mutating the test process's env is process-global and
  breaks parallelism.
- **No exact-stdout assertions.** Assert on observable side-effects
  (events on relays, refs on disk, git config keys, exit status), not
  on the literal text ngit prints. The handful of stdout reads that
  exist today (e.g. parsing the `clone url:` line out of `ngit init`)
  are tolerated as a regression-catching shortcut and must use
  case-insensitive prefix matching, never equality.
- **Push to a nostr remote via `Repo::nostr_push`, never
  `repo.git(["push", …])`.** `nostr_push` runs the push and then
  ticks one whole unix second so the next event-publishing operation
  lands in a strictly later `created_at` second than the
  auto-generated kind-30618 state event the push just emitted.
  Bypassing it produces a roughly 30% flake rate where the next
  publish hits the relay's "this event is deleted" check_id path on
  a same-second event-id collision. See
  `docs/architecture/test-harness.md` § "Timing rule" for the chain;
  `test_harness/src/clock.rs` for the writeup.

## Common Pitfalls

### 1. URL Normalization

**Problem**: Using wrong normalization function leads to incorrect comparisons.

**Solution**:

- For full URL comparison: Direct string comparison
- For server identification: Use `normalize_grasp_server_url()`

### 2. Async/Sync Boundaries

**Problem**: Mixing async and sync code incorrectly.

**Solution**: Use `tokio::runtime::Runtime` or proper async context.

### 3. Git State

**Problem**: Git state getting out of sync with nostr events.

**Solution**: Always update both git refs and publish nostr events atomically.

## Contribution Workflow

1. **Clone**: Use ngit itself to clone: `nostr://dan@gitworkshop.dev/relay.damus.io/ngit`
2. **Branch**: Create a branch with `pr/` prefix for pull requests
3. **Test**: Run `cargo test` and `cargo clippy`
4. **Commit**: Follow git commit message conventions (enforced by `git_hooks/commit-msg`)
5. **Push**: Push branch to submit PR via ngit

## Resources

- **Main Repository**: [gitworkshop.dev/dan@gitworkshop.dev/ngit]https://gitworkshop.dev/dan@gitworkshop.dev/ngit
- **Homepage**: [gitworkshop.dev/ngit]https://gitworkshop.dev/ngit
- **Source**: [codeberg.org/DanConwayDev/ngit-cli]https://codeberg.org/DanConwayDev/ngit-cli
- **Nostr Protocol**: [github.com/nostr-protocol/nostr]https://github.com/nostr-protocol/nostr

## Quick Reference

### Build Commands

```bash
cargo build                    # Development build
cargo build --release         # Release build
cargo test                    # Run tests
cargo clippy                  # Run linter
cargo fmt                     # Format code
```

### File Locations

```bash
src/lib/repo_ref.rs           # URL handling, grasp server detection
src/lib/client.rs             # Nostr client
src/lib/git_events.rs         # Git event handling
src/bin/ngit/main.rs          # Main CLI entry point
src/bin/git_remote_nostr/     # Git remote helper
tests/                        # Integration tests
```

### Key Functions

```rust
// URL handling
is_grasp_server_in_list(url, list)     // Check if URL in list (exact match)
is_grasp_server_clone_url(url)         // Check if URL is grasp server format
normalize_grasp_server_url(url)        // Extract server part from URL

// Repository operations
repo_ref.to_nostr_git_url()            // Convert to nostr URL
push_to_remote()                       // Push to git server
fetch_from_remote()                    // Fetch from git server
```

## Support and Questions

For questions about the codebase or contributions:

- Check existing issues on [gitworkshop.dev]https://gitworkshop.dev/dan@gitworkshop.dev/ngit
- Review test files for usage examples
- Examine integration tests for workflow examples

---

**Last Updated**: 2025-10-20  
**Version**: 1.7.4  
**Maintainer**: DanConwayDev