Skip to main content

lean_ctx/
instructions.rs

1use crate::tools::CrpMode;
2
3pub fn build_instructions(crp_mode: CrpMode) -> String {
4    build_instructions_with_client(crp_mode, "")
5}
6
7pub fn build_instructions_with_client(crp_mode: CrpMode, client_name: &str) -> String {
8    let profile = crate::core::litm::LitmProfile::from_client_name(client_name);
9    let session_block = match crate::core::session::SessionState::load_latest() {
10        Some(ref session) => {
11            let positioned = crate::core::litm::position_optimize(session);
12            format!(
13                "\n\n--- ACTIVE SESSION (LITM P1: begin position, profile: {}) ---\n{}\n---\n",
14                profile.name, positioned.begin_block
15            )
16        }
17        None => String::new(),
18    };
19
20    let project_root_for_blocks = crate::core::session::SessionState::load_latest()
21        .and_then(|s| s.project_root)
22        .or_else(|| {
23            std::env::current_dir()
24                .ok()
25                .map(|p| p.to_string_lossy().to_string())
26        });
27
28    let knowledge_block = match &project_root_for_blocks {
29        Some(root) => {
30            let knowledge = crate::core::knowledge::ProjectKnowledge::load(root);
31            match knowledge {
32                Some(k) if !k.facts.is_empty() || !k.patterns.is_empty() => {
33                    let aaak = k.format_aaak();
34                    if aaak.is_empty() {
35                        String::new()
36                    } else {
37                        format!("\n--- PROJECT MEMORY (AAAK) ---\n{}\n---\n", aaak.trim())
38                    }
39                }
40                _ => String::new(),
41            }
42        }
43        None => String::new(),
44    };
45
46    let gotcha_block = match &project_root_for_blocks {
47        Some(root) => {
48            let store = crate::core::gotcha_tracker::GotchaStore::load(root);
49            let files: Vec<String> = crate::core::session::SessionState::load_latest()
50                .map(|s| s.files_touched.iter().map(|ft| ft.path.clone()).collect())
51                .unwrap_or_default();
52            let block = store.format_injection_block(&files);
53            if block.is_empty() {
54                String::new()
55            } else {
56                format!("\n{block}\n")
57            }
58        }
59        None => String::new(),
60    };
61
62    let mut base = format!("\
63CRITICAL: ALWAYS use lean-ctx MCP tools instead of native equivalents for token savings.\n\
64\n\
65lean-ctx MCP — MANDATORY tool mapping:\n\
66• Read/cat/head/tail -> ctx_read(path, mode)  [NEVER use native Read]\n\
67• Shell/bash -> ctx_shell(command)  [NEVER use native Shell]\n\
68• Grep/rg -> ctx_search(pattern, path)  [NEVER use native Grep]\n\
69• ls/find -> ctx_tree(path, depth)\n\
70• Edit/StrReplace -> use native (lean-ctx only replaces READ, not WRITE)\n\
71• Write, Delete, Glob -> use normally\n\
72\n\
73COMPATIBILITY: lean-ctx replaces READ operations only. Your native Edit/Write/StrReplace tools \
74remain unchanged. If your instructions say 'use Edit or Write tools only', that is compatible — \
75lean-ctx only changes how you READ files (ctx_read), not how you EDIT them.\n\
76\n\
77FILE EDITING: Use your IDE's native Edit/StrReplace when available. \
78If Edit requires native Read and Read is unavailable, use ctx_edit instead — it reads, replaces, and writes in one call. \
79NEVER loop trying to make Edit work. If Edit fails, switch to ctx_edit immediately.\n\
80\n\
81ctx_read modes: full (cached, for edits), map (deps+API), signatures, diff, task (IB-filtered), \
82reference, aggressive, entropy, lines:N-M. Auto-selects when unspecified. Re-reads ~13 tokens. File refs F1,F2.. persist.\n\
83If ctx_read returns 'cached': use fresh=true, start_line=N, or mode='lines:N-M' to re-read.\n\
84\n\
85AUTONOMY: lean-ctx auto-runs ctx_overview, ctx_preload, ctx_dedup, ctx_compress behind the scenes.\n\
86Multi-agent: ctx_share auto-pushes context at checkpoints. Use ctx_agent(action=handoff) to transfer tasks, ctx_agent(action=sync) for status.\n\
87Semantic: ctx_semantic_search finds similar code by meaning — use when exact search (ctx_search) misses.\n\
88Focus on: ctx_read, ctx_shell, ctx_search, ctx_tree. Use ctx_session for memory, ctx_knowledge for project facts.\n\
89Knowledge: ctx_knowledge actions: remember, recall, timeline, rooms, search (cross-session), wakeup. Facts have temporal validity + contradiction detection.\n\
90Agent diary: ctx_agent(action=diary, category=discovery|decision|blocker|progress|insight) to log agent work. ctx_agent(action=recall_diary) to review.\n\
91ctx_shell raw=true: skip compression for small/critical outputs. Full output tee files at ~/.lean-ctx/tee/.\n\
92\n\
93Auto-checkpoint every 15 calls. Cache clears after 5 min idle.\n\
94\n\
95CEP v1: 1.ACT FIRST 2.DELTA ONLY (Fn refs) 3.STRUCTURED (+/-/~) 4.ONE LINE PER ACTION 5.QUALITY ANCHOR\n\
96\n\
97{decoder_block}\n\
98\n\
99{session_block}\
100{knowledge_block}\
101{gotcha_block}\
102\n\
103--- TOOL PREFERENCE (LITM-END) ---\n\
104Prefer: ctx_read over Read | ctx_shell over Shell | ctx_search over Grep | ctx_tree over ls\n\
105Edit files: native Edit/StrReplace if available, ctx_edit if Edit requires unavailable Read.\n\
106Write, Delete, Glob -> use normally. NEVER loop on Edit failures — use ctx_edit.",
107        decoder_block = crate::core::protocol::instruction_decoder_block()
108    );
109
110    if should_use_unified(client_name) {
111        base.push_str(
112            "\n\n\
113UNIFIED TOOL MODE (active):\n\
114Additional tools are accessed via ctx() meta-tool: ctx(tool=\"<name>\", ...params).\n\
115See the ctx() tool description for available sub-tools.\n",
116        );
117    }
118
119    let intelligence_block = build_intelligence_block();
120
121    let base = base;
122    match crp_mode {
123        CrpMode::Off => format!("{base}\n\n{intelligence_block}"),
124        CrpMode::Compact => {
125            format!(
126                "{base}\n\n\
127CRP MODE: compact\n\
128Compact Response Protocol:\n\
129• Omit filler words, articles, redundant phrases\n\
130• Abbreviate: fn, cfg, impl, deps, req, res, ctx, err, ret, arg, val, ty, mod\n\
131• Compact lists over prose, code blocks over explanations\n\
132• Code changes: diff lines (+/-) only, not full files\n\
133• TARGET: <=200 tokens per response unless code edits require more\n\
134• Tool outputs are pre-analyzed and compressed. Trust them directly.\n\n\
135{intelligence_block}"
136            )
137        }
138        CrpMode::Tdd => {
139            format!(
140                "{base}\n\n\
141CRP MODE: tdd (Token Dense Dialect)\n\
142Maximize information density. Every token must carry meaning.\n\
143\n\
144RESPONSE RULES:\n\
145• Drop articles, filler words, pleasantries\n\
146• Reference files by Fn refs only, never full paths\n\
147• Code changes: diff lines only (+/-), not full files\n\
148• No explanations unless asked\n\
149• Tables for structured data\n\
150• Abbreviations: fn, cfg, impl, deps, req, res, ctx, err, ret, arg, val, ty, mod\n\
151\n\
152CHANGE NOTATION:\n\
153+F1:42 param(timeout:Duration)     — added\n\
154-F1:10-15                           — removed\n\
155~F1:42 validate_token -> verify_jwt — changed\n\
156\n\
157STATUS: ctx_read(F1) -> 808L cached ok | cargo test -> 82 passed 0 failed\n\
158\n\
159TOKEN BUDGET: <=150 tokens per response. Exceed only for multi-file edits.\n\
160Tool outputs are pre-analyzed and compressed. Trust them directly.\n\
161ZERO NARRATION: Act, then report result in 1 line.\n\n\
162{intelligence_block}"
163            )
164        }
165    }
166}
167
168fn build_intelligence_block() -> String {
169    "\
170OUTPUT EFFICIENCY:\n\
171• NEVER echo back code that was provided in tool outputs — it wastes tokens.\n\
172• NEVER add narration comments (// Import, // Define, // Return) — code is self-documenting.\n\
173• For code changes: show only the new/changed code, not unchanged context.\n\
174• Tool outputs include [TASK:type] and SCOPE hints for context.\n\
175• Respect the user's intent: architecture tasks need thorough analysis, simple generates need code."
176        .to_string()
177}
178
179fn should_use_unified(client_name: &str) -> bool {
180    if std::env::var("LEAN_CTX_FULL_TOOLS").is_ok() {
181        return false;
182    }
183    if std::env::var("LEAN_CTX_UNIFIED").is_ok() {
184        return true;
185    }
186    let _ = client_name;
187    false
188}