Skip to main content

lean_ctx/tool_defs/
mod.rs

1use std::sync::Arc;
2
3use rmcp::model::*;
4use serde_json::{json, Map, Value};
5
6pub fn tool_def(name: &'static str, description: &'static str, schema_value: Value) -> Tool {
7    let schema: Map<String, Value> = match schema_value {
8        Value::Object(map) => map,
9        _ => Map::new(),
10    };
11    Tool::new(name, description, Arc::new(schema))
12}
13
14mod granular;
15pub use granular::granular_tool_defs;
16
17pub fn unified_tool_defs() -> Vec<Tool> {
18    vec![
19        tool_def(
20            "ctx_read",
21            "Read file (cached, compressed). Modes: full|map|signatures|diff|aggressive|entropy|task|reference|lines:N-M. fresh=true re-reads.",
22            json!({
23                "type": "object",
24                "properties": {
25                    "path": { "type": "string", "description": "File path" },
26                    "mode": { "type": "string" },
27                    "start_line": { "type": "integer" },
28                    "fresh": { "type": "boolean" }
29                },
30                "required": ["path"]
31            }),
32        ),
33        tool_def(
34            "ctx_shell",
35            "Run shell command (compressed output). raw=true skips compression. cwd sets working directory.",
36            json!({
37                "type": "object",
38                "properties": {
39                    "command": { "type": "string", "description": "Shell command" },
40                    "raw": { "type": "boolean", "description": "Skip compression for full output" },
41                    "cwd": { "type": "string", "description": "Working directory (defaults to last cd or project root)" }
42                },
43                "required": ["command"]
44            }),
45        ),
46        tool_def(
47            "ctx_search",
48            "Regex code search (.gitignore aware).",
49            json!({
50                "type": "object",
51                "properties": {
52                    "pattern": { "type": "string", "description": "Regex pattern" },
53                    "path": { "type": "string" },
54                    "ext": { "type": "string" },
55                    "max_results": { "type": "integer" },
56                    "ignore_gitignore": { "type": "boolean" }
57                },
58                "required": ["pattern"]
59            }),
60        ),
61        tool_def(
62            "ctx_tree",
63            "Directory listing with file counts.",
64            json!({
65                "type": "object",
66                "properties": {
67                    "path": { "type": "string" },
68                    "depth": { "type": "integer" },
69                    "show_hidden": { "type": "boolean" }
70                }
71            }),
72        ),
73        tool_def(
74            "ctx",
75            "Meta-tool: set tool= to sub-tool name. Sub-tools: compress (checkpoint), metrics (stats), \
76analyze (entropy), cache (status|clear|invalidate), discover (missed patterns), smart_read (auto-mode), \
77delta (incremental diff), dedup (cross-file), fill (budget-aware batch read), intent (auto-read by task), \
78response (compress LLM text), context (session state), graph (build|related|symbol|impact|status), \
79session (load|save|task|finding|decision|status|reset|list|cleanup), \
80knowledge (remember|recall|pattern|consolidate|timeline|rooms|search|wakeup|status|remove|export|embeddings_status|embeddings_reset|embeddings_reindex), \
81agent (register|post|read|status|list|info|diary|recall_diary|diaries), overview (project map), \
82wrapped (savings report), benchmark (file|project), multi_read (batch), semantic_search (BM25), \
83cost (attribution), heatmap (file access), impact (graph impact), architecture (graph structure), \
84task (A2A tasks), workflow (state machine).",
85            json!({
86                "type": "object",
87                "properties": {
88                    "tool": {
89                        "type": "string",
90                        "description": "compress|metrics|analyze|cache|discover|smart_read|delta|dedup|fill|intent|response|context|graph|session|knowledge|agent|overview|wrapped|benchmark|multi_read|semantic_search|cost|heatmap|impact|architecture|task|workflow"
91                    },
92                    "action": { "type": "string" },
93                    "path": { "type": "string" },
94                    "paths": { "type": "array", "items": { "type": "string" } },
95                    "query": { "type": "string" },
96                    "value": { "type": "string" },
97                    "category": { "type": "string" },
98                    "key": { "type": "string" },
99                    "to": { "type": "string" },
100                    "spec": { "type": "string" },
101                    "budget": { "type": "integer" },
102                    "task": { "type": "string" },
103                    "mode": { "type": "string" },
104                    "text": { "type": "string" },
105                    "message": { "type": "string" },
106                    "session_id": { "type": "string" },
107                    "period": { "type": "string" },
108                    "format": { "type": "string" },
109                    "agent_type": { "type": "string" },
110                    "role": { "type": "string" },
111                    "status": { "type": "string" },
112                    "pattern_type": { "type": "string" },
113                    "examples": { "type": "array", "items": { "type": "string" } },
114                    "confidence": { "type": "number" },
115                    "project_root": { "type": "string" },
116                    "include_signatures": { "type": "boolean" },
117                    "limit": { "type": "integer" },
118                    "to_agent": { "type": "string" },
119                    "task_id": { "type": "string" },
120                    "agent_id": { "type": "string" },
121                    "description": { "type": "string" },
122                    "state": { "type": "string" },
123                    "root": { "type": "string" },
124                    "depth": { "type": "integer" },
125                    "show_hidden": { "type": "boolean" }
126                },
127                "required": ["tool"]
128            }),
129        ),
130    ]
131}
132
133const CORE_TOOL_NAMES: &[&str] = &[
134    "ctx_read",
135    "ctx_multi_read",
136    "ctx_shell",
137    "ctx_search",
138    "ctx_tree",
139    "ctx_edit",
140    "ctx_session",
141    "ctx_knowledge",
142];
143
144pub fn lazy_tool_defs() -> Vec<Tool> {
145    let all = granular_tool_defs();
146    let mut core: Vec<Tool> = all
147        .into_iter()
148        .filter(|t| CORE_TOOL_NAMES.contains(&t.name.as_ref()))
149        .collect();
150
151    core.push(tool_def(
152        "ctx_discover_tools",
153        "Search available lean-ctx tools by keyword. Returns matching tool names + descriptions for on-demand loading.",
154        json!({
155            "type": "object",
156            "properties": {
157                "query": {
158                    "type": "string",
159                    "description": "Search keyword (e.g. 'graph', 'cost', 'workflow', 'dedup')"
160                }
161            },
162            "required": ["query"]
163        }),
164    ));
165
166    core
167}
168
169pub fn discover_tools(query: &str) -> String {
170    let all = list_all_tool_defs();
171    let query_lower = query.to_lowercase();
172    let matches: Vec<(&str, &str)> = all
173        .iter()
174        .filter(|(name, desc, _)| {
175            name.to_lowercase().contains(&query_lower) || desc.to_lowercase().contains(&query_lower)
176        })
177        .map(|(name, desc, _)| (*name, *desc))
178        .collect();
179
180    if matches.is_empty() {
181        return format!("No tools found matching '{query}'. Try broader terms like: graph, cost, session, search, compress, agent, workflow, gain.");
182    }
183
184    let mut out = format!("{} tools matching '{query}':\n", matches.len());
185    for (name, desc) in &matches {
186        let short = if desc.len() > 80 { &desc[..80] } else { desc };
187        out.push_str(&format!("  {name} — {short}\n"));
188    }
189    out.push_str("\nCall the tool directly by name to use it.");
190    out
191}
192
193pub fn is_lazy_mode() -> bool {
194    std::env::var("LEAN_CTX_LAZY_TOOLS").is_ok()
195}
196
197pub fn list_all_tool_defs() -> Vec<(&'static str, &'static str, Value)> {
198    vec![
199        ("ctx_read", "Read file (cached, compressed). Re-reads ~13 tok. Auto-selects optimal mode. \
200Modes: full|map|signatures|diff|aggressive|entropy|task|reference|lines:N-M. fresh=true re-reads.", json!({"type": "object", "properties": {"path": {"type": "string"}, "mode": {"type": "string"}, "start_line": {"type": "integer"}, "fresh": {"type": "boolean"}}, "required": ["path"]})),
201        ("ctx_multi_read", "Batch read files in one call. Same modes as ctx_read.", json!({"type": "object", "properties": {"paths": {"type": "array", "items": {"type": "string"}}, "mode": {"type": "string"}}, "required": ["paths"]})),
202        ("ctx_tree", "Directory listing with file counts.", json!({"type": "object", "properties": {"path": {"type": "string"}, "depth": {"type": "integer"}, "show_hidden": {"type": "boolean"}}})),
203        ("ctx_shell", "Run shell command (compressed output, 90+ patterns). cwd sets working directory.", json!({"type": "object", "properties": {"command": {"type": "string"}, "cwd": {"type": "string", "description": "Working directory"}}, "required": ["command"]})),
204        ("ctx_search", "Regex code search (.gitignore aware, compact results).", json!({"type": "object", "properties": {"pattern": {"type": "string"}, "path": {"type": "string"}, "ext": {"type": "string"}, "max_results": {"type": "integer"}}, "required": ["pattern"]})),
205        ("ctx_compress", "Context checkpoint for long conversations.", json!({"type": "object", "properties": {"include_signatures": {"type": "boolean"}}})),
206        ("ctx_benchmark", "Benchmark compression modes for a file or project.", json!({"type": "object", "properties": {"path": {"type": "string"}, "action": {"type": "string"}, "format": {"type": "string"}}, "required": ["path"]})),
207        ("ctx_metrics", "Session token stats, cache rates, per-tool savings.", json!({"type": "object", "properties": {}})),
208        ("ctx_analyze", "Entropy analysis — recommends optimal compression mode for a file.", json!({"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]})),
209        ("ctx_cache", "Cache ops: status|clear|invalidate.", json!({"type": "object", "properties": {"action": {"type": "string"}, "path": {"type": "string"}}, "required": ["action"]})),
210        ("ctx_discover", "Find missed compression opportunities in shell history.", json!({"type": "object", "properties": {"limit": {"type": "integer"}}})),
211        ("ctx_smart_read", "Auto-select optimal read mode for a file.", json!({"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]})),
212        ("ctx_delta", "Incremental diff — sends only changed lines since last read.", json!({"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]})),
213        ("ctx_edit", "Edit a file via search-and-replace. Works without native Read/Edit tools. Use when Edit requires Read but Read is unavailable.", json!({"type": "object", "properties": {"path": {"type": "string"}, "old_string": {"type": "string"}, "new_string": {"type": "string"}, "replace_all": {"type": "boolean"}, "create": {"type": "boolean"}}, "required": ["path", "new_string"]})),
214        ("ctx_dedup", "Cross-file dedup: analyze or apply shared block references.", json!({"type": "object", "properties": {"action": {"type": "string"}}})),
215        ("ctx_fill", "Budget-aware context fill — auto-selects compression per file within token limit.", json!({"type": "object", "properties": {"paths": {"type": "array", "items": {"type": "string"}}, "budget": {"type": "integer"}, "task": {"type": "string"}}, "required": ["paths", "budget"]})),
216        ("ctx_intent", "Structured intent input (optional) — submit compact JSON or short text; server also infers intents automatically from tool calls.", json!({"type": "object", "properties": {"query": {"type": "string"}, "project_root": {"type": "string"}}, "required": ["query"]})),
217        ("ctx_response", "Compress LLM response text (remove filler, apply TDD).", json!({"type": "object", "properties": {"text": {"type": "string"}}, "required": ["text"]})),
218        ("ctx_context", "Session context overview — cached files, seen files, session state.", json!({"type": "object", "properties": {}})),
219        ("ctx_graph", "Code dependency graph. Actions: build (index project), related (find files connected to path), \
220symbol (lookup definition/usages as file::name), impact (blast radius of changes to path), status (index stats).", json!({"type": "object", "properties": {"action": {"type": "string"}, "path": {"type": "string"}, "project_root": {"type": "string"}}, "required": ["action"]})),
221        ("ctx_session", "Cross-session memory (CCP). Actions: load (restore previous session ~400 tok), \
222save, status, task (set current task), finding (record discovery), decision (record choice), \
223reset, list (show sessions), cleanup, snapshot (build compaction snapshot ~2KB), \
224restore (rebuild state from snapshot after context compaction).", json!({"type": "object", "properties": {"action": {"type": "string"}, "value": {"type": "string"}, "session_id": {"type": "string"}}, "required": ["action"]})),
225        ("ctx_knowledge", "Persistent project knowledge with temporal facts + contradiction detection. Actions: remember (auto-tracks validity + detects contradictions), recall, pattern, consolidate, \
226gotcha (record a bug to never repeat — trigger+resolution), timeline (fact version history), rooms (list knowledge categories), \
227search (cross-session/cross-project), wakeup (compact AAAK briefing), status, remove, export, embeddings_status|embeddings_reset|embeddings_reindex.", json!({"type": "object", "properties": {"action": {"type": "string"}, "category": {"type": "string"}, "key": {"type": "string"}, "value": {"type": "string"}, "query": {"type": "string"}, "trigger": {"type": "string"}, "resolution": {"type": "string"}, "severity": {"type": "string"}}, "required": ["action"]})),
228        ("ctx_agent", "Multi-agent coordination with persistent diaries. Actions: register, \
229post, read, status, handoff, sync, diary (log discovery/decision/blocker/progress/insight — persisted), \
230recall_diary (read diary), diaries (list all), list, info.", json!({"type": "object", "properties": {"action": {"type": "string"}, "agent_type": {"type": "string"}, "role": {"type": "string"}, "message": {"type": "string"}, "to_agent": {"type": "string"}, "status": {"type": "string"}}, "required": ["action"]})),
231        ("ctx_share", "Share cached file contexts between agents. Actions: push (share files from cache), \
232pull (receive shared files), list (show all shared contexts), clear (remove your shared contexts).", json!({"type": "object", "properties": {"action": {"type": "string"}, "paths": {"type": "string"}, "to_agent": {"type": "string"}, "message": {"type": "string"}}, "required": ["action"]})),
233        ("ctx_overview", "Task-relevant project map — use at session start.", json!({"type": "object", "properties": {"task": {"type": "string"}, "path": {"type": "string"}}})),
234        ("ctx_preload", "Proactive context loader — reads and caches task-relevant files, returns compact L-curve-optimized summary with critical lines, imports, and signatures. Costs ~50-100 tokens instead of ~5000 for individual reads.", json!({"type": "object", "properties": {"task": {"type": "string", "description": "Task description (e.g. 'fix auth bug in validate_token')"}, "path": {"type": "string", "description": "Project root (default: .)"}}, "required": ["task"]})),
235        ("ctx_prefetch", "Predictive prefetch — prewarm cache for blast radius files (graph + task signals) within budgets.", json!({"type": "object", "properties": {"root": {"type": "string"}, "task": {"type": "string"}, "changed_files": {"type": "array", "items": {"type": "string"}}, "budget_tokens": {"type": "integer"}, "max_files": {"type": "integer"}}})),
236        ("ctx_wrapped", "Savings report card. Periods: week|month|all.", json!({"type": "object", "properties": {"period": {"type": "string"}}})),
237        ("ctx_cost", "Cost attribution (local-first). Actions: report|agent|tools|json|reset.", json!({"type": "object", "properties": {"action": {"type": "string"}, "agent_id": {"type": "string"}, "limit": {"type": "integer"}}})),
238        ("ctx_gain", "Gain report.", json!({"type": "object", "properties": {"action": {"type": "string"}, "period": {"type": "string"}, "model": {"type": "string"}, "limit": {"type": "integer"}}})),
239        ("ctx_feedback", "Harness feedback for LLM output tokens/latency (local-first). Actions: record|report|json|reset|status.", json!({"type": "object", "properties": {"action": {"type": "string"}, "agent_id": {"type": "string"}, "intent": {"type": "string"}, "model": {"type": "string"}, "llm_input_tokens": {"type": "integer"}, "llm_output_tokens": {"type": "integer"}, "latency_ms": {"type": "integer"}, "note": {"type": "string"}, "limit": {"type": "integer"}}})),
240        ("ctx_handoff", "Context Ledger Protocol (hashed, deterministic, local-first). Actions: create|show|list|pull|clear.", json!({"type": "object", "properties": {"action": {"type": "string"}, "path": {"type": "string"}, "paths": {"type": "array", "items": {"type": "string"}}, "apply_workflow": {"type": "boolean"}, "apply_session": {"type": "boolean"}, "apply_knowledge": {"type": "boolean"}}})),
241        ("ctx_heatmap", "File access heatmap (local-first). Actions: status|directory|cold|json.", json!({"type": "object", "properties": {"action": {"type": "string"}, "path": {"type": "string"}}})),
242        ("ctx_task", "Multi-agent task orchestration. Actions: create|update|list|get|cancel|message|info.", json!({"type": "object", "properties": {"action": {"type": "string"}, "task_id": {"type": "string"}, "to_agent": {"type": "string"}, "description": {"type": "string"}, "state": {"type": "string"}, "message": {"type": "string"}}, "required": ["action"]})),
243        ("ctx_impact", "Graph-based impact analysis. Actions: analyze|chain|build|status.", json!({"type": "object", "properties": {"action": {"type": "string"}, "path": {"type": "string"}, "root": {"type": "string"}, "depth": {"type": "integer"}}})),
244        ("ctx_architecture", "Graph-based architecture analysis. Actions: overview|clusters|layers|cycles|entrypoints|module.", json!({"type": "object", "properties": {"action": {"type": "string"}, "path": {"type": "string"}, "root": {"type": "string"}}})),
245        ("ctx_workflow", "Workflow rails (state machine + evidence). Actions: start|status|transition|complete|evidence_add|evidence_list|stop.", json!({"type": "object", "properties": {"action": {"type": "string"}, "name": {"type": "string"}, "spec": {"type": "string"}, "to": {"type": "string"}, "key": {"type": "string"}, "value": {"type": "string"}}})),
246        ("ctx_semantic_search", "Semantic code search (BM25 + optional embeddings/hybrid). action=reindex to rebuild.", json!({"type": "object", "properties": {"query": {"type": "string"}, "path": {"type": "string"}, "top_k": {"type": "integer"}, "action": {"type": "string"}, "mode": {"type": "string", "enum": ["bm25","dense","hybrid"]}, "languages": {"type": "array", "items": {"type": "string"}}, "path_glob": {"type": "string"}}, "required": ["query"]})),
247        ("ctx_execute", "Run code in sandbox (11 languages). Only stdout enters context. Languages: javascript, typescript, python, shell, ruby, go, rust, php, perl, r, elixir. Actions: batch (multiple scripts), file (process file in sandbox).", json!({"type": "object", "properties": {"language": {"type": "string"}, "code": {"type": "string"}, "intent": {"type": "string"}, "timeout": {"type": "integer"}, "action": {"type": "string"}, "items": {"type": "string"}, "path": {"type": "string"}}, "required": ["language", "code"]})),
248        ("ctx_symbol", "Read a specific symbol (function, struct, class) by name. Returns only the symbol code block instead of the entire file. 90-97% fewer tokens than full file read.", json!({"type": "object", "properties": {"name": {"type": "string"}, "file": {"type": "string"}, "kind": {"type": "string"}}, "required": ["name"]})),
249        ("ctx_outline", "List all symbols in a file with signatures. Much fewer tokens than reading the full file.", json!({"type": "object", "properties": {"path": {"type": "string"}, "kind": {"type": "string"}}, "required": ["path"]})),
250        ("ctx_compress_memory", "Compress a memory/config file (CLAUDE.md, .cursorrules) preserving code, URLs, paths. Creates .original.md backup.", json!({"type": "object", "properties": {"path": {"type": "string"}}, "required": ["path"]})),
251        ("ctx_callers", "Find all symbols that call a given function/method.", json!({"type": "object", "properties": {"symbol": {"type": "string"}, "file": {"type": "string"}}, "required": ["symbol"]})),
252        ("ctx_callees", "Find all functions/methods called by a given symbol.", json!({"type": "object", "properties": {"symbol": {"type": "string"}, "file": {"type": "string"}}, "required": ["symbol"]})),
253        ("ctx_routes", "List HTTP routes/endpoints extracted from the project. Supports Express, Flask, FastAPI, Actix, Spring, Rails, Next.js.", json!({"type": "object", "properties": {"method": {"type": "string"}, "path": {"type": "string"}}})),
254        ("ctx_graph_diagram", "Generate a Mermaid diagram of the dependency or call graph.", json!({"type": "object", "properties": {"file": {"type": "string"}, "depth": {"type": "integer"}, "kind": {"type": "string"}}})),
255    ]
256}