Skip to content
Closed
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
39 changes: 36 additions & 3 deletions .codescout/memories/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ src/
│ ├── git.rs # git_blame, file_log (not registered; used by dashboard)
│ ├── semantic.rs # semantic_search, index_project, index_status
│ ├── github.rs # github_identity, github_issue, github_pr, github_file, github_repo
│ ├── library.rs # list_libraries
│ ├── library.rs # list_libraries, register_library
│ ├── memory.rs # memory (action: read/write/list/delete/remember/recall/forget/refresh_anchors)
│ ├── usage.rs # GetUsageStats (dashboard API; not an MCP tool)
│ ├── ast.rs # list_functions, list_docs (not registered; tree-sitter offline tools)
Expand All @@ -48,7 +48,7 @@ src/
1. `find_tool(name)` — linear scan over `Vec<Arc<dyn Tool>>`
2. `check_tool_access(name, &security)` — denormalized match-arm gate in
`src/util/path_security.rs`. Missing a write tool here bypasses access controls.
3. Build `ToolContext { agent, lsp, output_buffer, progress }`
3. Build `ToolContext { agent, lsp, output_buffer, progress, peer, section_coverage }`
4. Apply `tool_timeout_secs` from `project.toml` (skipped for `index_project`, `onboarding`)
5. `UsageRecorder::record_content` wraps `tool.call_content()`
6. `route_tool_error`: `RecoverableError` → `isError:false` + JSON error/hint;
Expand Down Expand Up @@ -81,7 +81,8 @@ src/
`build_index(root, force)` in `src/embed/index.rs`:
1. `find_changed_files()`: git diff → mtime → SHA-256 fallback
2. `ast_chunker::split_file()`: AST-aware chunking per language, respects chunk size config
3. Concurrent embedding with semaphore cap=4 (`JoinSet` over `Embedder::embed()`)\n4. Single SQLite transaction: delete old chunks, insert new, upsert file hash
3. Concurrent embedding with semaphore cap=4 (`JoinSet` over `Embedder::embed()`)
4. Single SQLite transaction: delete old chunks, insert new, upsert file hash
5. Drift detection (if enabled): cosine distance old→new embeddings → `drift_report` table
6. High-drift files → mark memory anchors stale

Expand All @@ -98,6 +99,38 @@ chunk embeddings into memory for each query — known perf issue for large index
referenced in content. `project_status` checks SHA-256 of anchored files to surface
stale memories. Regenerated on each `write`; cleared via `refresh_anchors` action.

## LSP Multiplexer (Kotlin-specific)

The `mux` subcommand (`--hidden` in CLI) spawns a Unix-socket-based multiplexer for
languages that support only one server instance per workspace (Kotlin's kotlin-lsp).

Flow:
- `LspManager::do_start(language, root)` for mux-enabled languages calls
`ensure_mux_running(language, root)` in `src/lsp/mux/process.rs`
- Mux socket path: `/tmp/codescout-<lang>-mux-<workspace_hash>.sock`
- Lock path: `/tmp/codescout-<lang>-mux-<workspace_hash>.lock`
- Auto-terminates after `idle_timeout` (default 300s) with no connected clients
- Multiple codescout instances share one kotlin-lsp process via the mux socket

Kotlin-specific: passes `--system-path=/tmp/codescout-mux-kotlin-lsp` and
`GRADLE_USER_HOME=/tmp/codescout-mux-gradle` to isolate from per-PID paths.

## ToolContext Fields

`ToolContext` (src/tools/mod.rs):
| Field | Type | Description |
|-------|------|-------------|
| `agent` | `Agent` | Shared state: project config, memories, registry |
| `lsp` | `Arc<dyn LspProvider>` | Mock-swappable LSP access |
| `output_buffer` | `Arc<OutputBuffer>` | Session LRU store for @tool_* refs |
| `progress` | `Option<Arc<ProgressReporter>>` | MCP progress notifications |
| `peer` | `Option<Peer<RoleServer>>` | MCP peer for elicitation; None in tests |
| `section_coverage` | `Arc<Mutex<SectionCoverage>>` | Tracks markdown sections read in session |

`ctx.elicit::<T>(message)` sends an MCP elicitation request to the user. Returns
`Ok(Some(T))` on response, `Ok(None)` if client doesn't support it, or
`RecoverableError` if user declined/cancelled.

## Unregistered Tool Structs

Several tool structs exist in code but are NOT registered in `from_parts`:
Expand Down
4 changes: 2 additions & 2 deletions .codescout/memories/project-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ rather than raw shell reads on source files.

## Tech Stack
- **Language:** Rust 1.75+ (MSRV enforced in CI)
- **MCP SDK:** `rmcp 0.1` (stdio + SSE transports)
- **MCP SDK:** `rmcp 1.x` (stdio + HTTP transports; builder pattern for `StreamableHttpServerConfig`)
- **LSP:** JSON-RPC clients for 9 languages via `lsp-types 0.97`
- **AST:** `tree-sitter` with grammars for Rust/Python/TypeScript/Go/Java/Kotlin
- **Embeddings:** SQLite via `rusqlite` (bundled); `remote-embed` (reqwest, Ollama/OpenAI-compatible) or `local-embed` (fastembed ONNX) — feature flags
Expand All @@ -29,4 +29,4 @@ rather than raw shell reads on source files.
- `e2e-*`: integration tests requiring real LSP servers installed

## Current Version
v0.2.2 (see `Cargo.toml`). Published to crates.io from `master` branch only.
v0.7.2 (see `Cargo.toml`). Published to crates.io from `master` branch only.
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ See `did_change_refreshes_stale_symbol_positions` in `src/lsp/client.rs` for the

## Key Patterns

**Tool trait** (`src/tools/mod.rs`): Each tool is a struct implementing `name()`, `description()`, `input_schema()`, `async call(Value, &ToolContext) -> Result<Value>`. 29 tools registered. All use `#[async_trait]`.
**Tool trait** (`src/tools/mod.rs`): Each tool is a struct implementing `name()`, `description()`, `input_schema()`, `async call(Value, &ToolContext) -> Result<Value>`. 27 tools registered. All use `#[async_trait]`.

**Tool↔MCP bridge** (`src/server.rs`): Tools registered as `Vec<Arc<dyn Tool>>`, dispatched dynamically in `call_tool`. Errors are routed through `route_tool_error`:
- `RecoverableError` (`src/tools/mod.rs`) → `isError: false` with JSON `{"error":"…","hint":"…"}` — LLM sees the problem and a corrective hint, **sibling parallel calls are not aborted by Claude Code**.
Expand Down
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "codescout"
version = "0.7.1"
version = "0.7.2"
edition = "2021"
description = "High-performance coding agent toolkit MCP server"
repository = "https://github.com/mareurs/codescout"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ cargo build
./target/debug/codescout start --project /path/to/code
```

Add codescout as an MCP server in Claude Code `settings.json`:
Add codescout as an MCP server `~/.claude.json`:

```json
{
Expand Down
41 changes: 35 additions & 6 deletions docs/agents/claude-code.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,53 @@
# Claude Code

## One-Time Setup
## Setup

Prerequisites: Rust toolchain, `cargo install codescout`. The binary lands at `~/.cargo/bin/codescout`.
Prerequisites: Rust toolchain

Register codescout as an MCP server. The recommended approach is user-level registration — edit `~/.claude/settings.json`:
### Using `cargo install` (recommended)

Install the binary:

```bash
cargo install codescout
# lands at ~/.cargo/bin/codescout
```

For this route, we can register as a user-level MCP server via the CLI:

```bash
claude mcp add --scope user codescout ~/.cargo/bin/codescout -- start
```

This writes the entry into `~/.claude.json` (user-scoped).


For a project-scoped alternative, place a `.mcp.json` file at the project root:

```json
{
"mcpServers": {
"codescout": {
"command": "codescout",
"args": ["start"],
"command": "/absolute/path/to/target/release/codescout",
"args": ["start", "--project", "/absolute/path/to/your/project"],
"type": "stdio"
}
}
}
```

For a project-scoped alternative, place a `.mcp.json` file at the project root with the same block.
Claude Code detects `.mcp.json` automatically and prompts for approval on first use.

### Using a local build

If you're working from source or want to pin to a specific local build:

```bash
cargo build --release
# binary at target/release/codescout
```

If you opt for the local build, manually add to either json based on desired scope. Use absolute paths — relative paths are not reliable here.

## Workflow Skills

Expand Down
2 changes: 1 addition & 1 deletion src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub struct CodeScoutServer {
agent: Agent,
lsp: Arc<dyn LspProvider>,
output_buffer: Arc<crate::tools::output_buffer::OutputBuffer>,
// Arc<dyn Tool>: heterogeneous collection of 23+ different tool types dispatched by name at runtime.
// Arc<dyn Tool>: heterogeneous collection of 27 registered tool types dispatched by name at runtime.
tools: Vec<Arc<dyn Tool>>,
/// Pre-computed at construction because `get_info()` is sync.
/// Becomes stale if project state changes mid-session (e.g. after onboarding or indexing).
Expand Down
Loading