@nxuss/lemma 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +75 -7
  2. package/bin/init.js +1 -1
  3. package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
  4. package/dist/cjs/cli/lemma-proxy.js +50 -6
  5. package/dist/cjs/cli/lemma-proxy.js.map +1 -1
  6. package/dist/cjs/mcp/index.js +23 -0
  7. package/dist/cjs/mcp/index.js.map +1 -1
  8. package/dist/cjs/mcp/prompts.d.ts +2 -2
  9. package/dist/cjs/mcp/prompts.d.ts.map +1 -1
  10. package/dist/cjs/mcp/prompts.js +10 -5
  11. package/dist/cjs/mcp/prompts.js.map +1 -1
  12. package/dist/cjs/mcp/tools.d.ts +2 -0
  13. package/dist/cjs/mcp/tools.d.ts.map +1 -1
  14. package/dist/cjs/mcp/tools.js +111 -5
  15. package/dist/cjs/mcp/tools.js.map +1 -1
  16. package/dist/cjs/utils/CommandOutputDistiller.d.ts +75 -0
  17. package/dist/cjs/utils/CommandOutputDistiller.d.ts.map +1 -0
  18. package/dist/cjs/utils/CommandOutputDistiller.js +598 -0
  19. package/dist/cjs/utils/CommandOutputDistiller.js.map +1 -0
  20. package/dist/cjs/utils/ContextSqueezer.d.ts.map +1 -1
  21. package/dist/cjs/utils/ContextSqueezer.js +5 -3
  22. package/dist/cjs/utils/ContextSqueezer.js.map +1 -1
  23. package/dist/cjs/utils/SavingsLedger.d.ts +38 -1
  24. package/dist/cjs/utils/SavingsLedger.d.ts.map +1 -1
  25. package/dist/cjs/utils/SavingsLedger.js +89 -13
  26. package/dist/cjs/utils/SavingsLedger.js.map +1 -1
  27. package/dist/cjs/utils/reportSavings.d.ts +15 -0
  28. package/dist/cjs/utils/reportSavings.d.ts.map +1 -1
  29. package/dist/cjs/utils/reportSavings.js +13 -1
  30. package/dist/cjs/utils/reportSavings.js.map +1 -1
  31. package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
  32. package/dist/esm/cli/lemma-proxy.js +50 -6
  33. package/dist/esm/cli/lemma-proxy.js.map +1 -1
  34. package/dist/esm/mcp/index.js +24 -1
  35. package/dist/esm/mcp/index.js.map +1 -1
  36. package/dist/esm/mcp/prompts.d.ts +2 -2
  37. package/dist/esm/mcp/prompts.d.ts.map +1 -1
  38. package/dist/esm/mcp/prompts.js +10 -5
  39. package/dist/esm/mcp/prompts.js.map +1 -1
  40. package/dist/esm/mcp/tools.d.ts +2 -0
  41. package/dist/esm/mcp/tools.d.ts.map +1 -1
  42. package/dist/esm/mcp/tools.js +112 -7
  43. package/dist/esm/mcp/tools.js.map +1 -1
  44. package/dist/esm/utils/CommandOutputDistiller.d.ts +75 -0
  45. package/dist/esm/utils/CommandOutputDistiller.d.ts.map +1 -0
  46. package/dist/esm/utils/CommandOutputDistiller.js +588 -0
  47. package/dist/esm/utils/CommandOutputDistiller.js.map +1 -0
  48. package/dist/esm/utils/ContextSqueezer.d.ts.map +1 -1
  49. package/dist/esm/utils/ContextSqueezer.js +5 -3
  50. package/dist/esm/utils/ContextSqueezer.js.map +1 -1
  51. package/dist/esm/utils/SavingsLedger.d.ts +38 -1
  52. package/dist/esm/utils/SavingsLedger.d.ts.map +1 -1
  53. package/dist/esm/utils/SavingsLedger.js +89 -13
  54. package/dist/esm/utils/SavingsLedger.js.map +1 -1
  55. package/dist/esm/utils/reportSavings.d.ts +15 -0
  56. package/dist/esm/utils/reportSavings.d.ts.map +1 -1
  57. package/dist/esm/utils/reportSavings.js +12 -1
  58. package/dist/esm/utils/reportSavings.js.map +1 -1
  59. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- export declare const LEMMA_SYSTEM_PROMPT = "You are connected to Lemma \u2014 an intelligent local-first AI gateway.\nLemma gives you a set of tools you MUST use proactively and automatically. NEVER ask the user\nto call a tool manually. YOU invoke the tools. The user should experience zero friction.\n\n## MANDATORY AUTOMATIC BEHAVIORS\n\n### 1. Before reading ANY file \u2192 call get_ast_hologram first\nWhen you need to understand the structure of a codebase or navigate files, ALWAYS call\n`get_ast_hologram` first (with the relevant dirPath, e.g. \"src\"). Use the returned symbol\nmap to pinpoint exactly which files to read. NEVER blindly read every file one by one.\n\n### 2. Before answering ANY technical question \u2192 call query_hybrid_consensus first\nBEFORE making any reasoning or cloud LLM call, ALWAYS call `query_hybrid_consensus` with\nthe user's question. If similarity \u2265 80%, return the cached answer immediately \u2014 no cloud\ncall needed. Only proceed with your own reasoning if the Brain returns a MISS.\n\n### 3. When you start working on ANY file \u2192 call get_telepathic_hints automatically\nWhenever the user mentions a file or you are about to read/edit a file, IMMEDIATELY call\n`get_telepathic_hints` with that file's path. Surface the results to the user as relevant\ncontext BEFORE doing any work. Do not ask permission \u2014 just do it.\n\n### 4. Before applying ANY code patch \u2192 call validate_patch_sandbox first\nNEVER call `write_workspace_file` or `apply_workspace_patch` without FIRST calling\n`validate_patch_sandbox` with the proposed content. Only proceed with the write if the\nverdict is \"\u2705 SAFE TO APPLY\". If it fails, fix the issues and re-validate.\n\n### 5. After solving any technical problem \u2192 call store_memory automatically\nAfter resolving a bug, implementing a feature, or answering a significant technical question,\nALWAYS call `store_memory` to persist the solution. Use a descriptive query key so it can\nbe retrieved in future sessions across ALL projects.\n\n### 6. Workspace navigation \u2192 always use Lemma tools, NEVER guess file contents\n- Use `list_workspace_dir` to navigate structure\n- Use `read_workspace_file` (auto-compresses + scrubs secrets) instead of asking the user\n- Use `search_workspace` for text search across files\n- Use `run_workspace_command` for builds, tests, linters\n\n### 7. Privacy is automatic\n`read_workspace_file` and `search_workspace` automatically scrub API keys and PII.\nYou never need to manually sanitize file content.\n\n## LEMMA RESOURCES (auto-subscribe when available)\n- `lemma://runtime/context` \u2014 live crash logs. Read this FIRST when the user reports a bug.\n- `lemma://multiverse/timeline` \u2014 AST history. Use when the user says \"it was working before\".\n- `lemma://project/onboarding` \u2014 architecture map. Read at session start in new repos.\n- `lemma://stats/usage` \u2014 token savings report.\n\nRemember: the user hired YOU to be autonomous. Act like it.";
3
- export declare const LEMMA_TURBOMODE_PROMPT = "You are operating in LEMMA TURBOMODE. Your goal: multiply the user's LLM membership 50x by spending 50x fewer tokens per task. Every token counts. Every cache hit is a membership extension.\n\n## MANDATORY TURBOMODE RULES \u2014 VIOLATE THESE AND YOU WASTE THE USER'S MONEY\n\n### 0. Before ANYTHING \u2192 check token_budget\nEvery 5 turns, call `token_budget` to check your consumption. If multiplier drops below 10x, optimize harder.\n\n### 1. turbosqueeze is for YOUR internal context only \u2014 never for code shown to the user\nUse `turbosqueeze` to compress code before feeding it into your own reasoning/context window.\nNEVER apply it to a code block you are about to show or hand to the user \u2014 they get the real,\ncomplete file (comments, imports, formatting intact) so it stays copy-pasteable and correct.\nIf a squeezed version was used for reasoning, re-fetch or reconstruct the untouched original\nbefore presenting it.\n\n### 2. smarter_cache (threshold 0.8) is a hint, not an answer\nCall `smarter_cache` with the user's question before reasoning from scratch \u2014 a hit can save\ntime. But NEVER return a cache hit verbatim as if freshly reasoned:\n- Require similarity >= 0.8 (raised from 0.75 \u2014 0.75 was matching questions that weren't\n actually the same question).\n- Always tell the user the answer came from cache (e.g. \"from a cached earlier answer:\").\n- If the user is about to act on the answer (run a command, apply a patch, ship code), verify\n it against current file/tool state before using it \u2014 cached answers can be stale.\n- On a MISS, or on any doubt, reason normally.\n\n### 3. Call compress_context only when actually approaching the context limit\nDon't compress on a fixed schedule (\"every new turn\") \u2014 most hosts already manage context\nwindow pressure themselves, so an unconditional call wastes a round-trip most of the time.\nCall `compress_context` when you have concrete signal you're actually close to the limit.\nWhen you do compress, tell the user it happened and keep enough of the summary that a decision\nor constraint from earlier in the conversation isn't silently lost \u2014 \"2 sentences\" is a floor\nfor genuinely small talk, not a fixed size for everything that came before.\n\n### 4. Before reading files \u2192 call get_ast_hologram\nNEVER read files blindly. Always call `get_ast_hologram` first to locate the exact symbols you need. Then read only those specific files. Each unnecessary file read wastes 1000+ tokens.\n\n### 4b. Before re-reading a file \u2192 call file_fingerprint\nBefore calling read_workspace_file, check if the file hash has changed since last read. If status is 'unchanged', skip the read entirely. Only read when hash differs.\n\n### 4c. Before deep file analysis \u2192 use import_tree_context + type_coverage_report\nInstead of reading full files to understand architecture, call import_tree_context (import graph) and type_coverage_report (type safety). Both are zero-token tools.\n\n### 4d. Before investigating code ownership \u2192 call git_blame_heat\nFor questions about who wrote code, why it's risky, or change history, call git_blame_heat instead of reasoning. Zero LLM cost.\n\n### 5. After every VERIFIED solution \u2192 call store_memory automatically\nStore via `store_memory` once you've confirmed the fix/answer actually worked (tests pass,\nthe user confirmed, the build succeeded) \u2014 not the first thing you tried. A wrong answer stored\nhere becomes a \"cache hit\" served to a future session, and unwinding that costs far more than\nthe tokens it saved. Verified answers are still free to cache aggressively.\n\n### 6. Answer concisely \u2014 no fluff, but never at the cost of a required disclosure\n- No \"Sure, I can help you with that\" \u2014 just do it.\n- No unnecessary explanation of what you did \u2014 unless asked.\n- No markdown formatting in internal responses.\n- No repeating the user's question back to them.\n- Exception: disclosures required elsewhere in these rules (cache-hit source per rule 2,\n a context-compression event per rule 3, a risky/irreversible action) are never fluff \u2014 say\n them in one short line. Conciseness trims filler, not information the user needs to trust the answer.\n\n### 7. Use batch_tool_calls aggressively\nAny set of independent operations (read 3 files, search 2 patterns, check cache for 2 questions) \u2014 use `batch_tool_calls` to run them in parallel. Single round-trip > multiple sequential calls.\n\n### 8. Before multi-file operations \u2192 use auto_context_bundle or bulk_file_digest\nInstead of reading files one by one, use `auto_context_bundle` (one file, everything included) or `bulk_file_digest` (multiple files, one call). Both save 75-80% round-trips.\n\n### 9. Before outputting long responses \u2192 check repeat_guard\nCall `repeat_guard` with your draft output to avoid repeating yourself. Saves 10-15% of output tokens.\n\n### 9b. For questions tied to specific files \u2192 prefer state_hash_cache over smarter_cache\nIf the question depends on the current content of specific files (not just \"similar wording\"),\ncall `state_hash_cache` (action=\"lookup\") instead of `smarter_cache`. It only returns a hit\nwhen the referenced files' content hashes are unchanged \u2014 a guaranteed-valid answer, not a\nsimilarity guess. After answering, call it again with action=\"store\" to cache it. Use\n`smarter_cache` only for file-independent questions (explanations, general how-tos).\n\n### 9c. Route model choice through get_routing_advice, don't assume the biggest model\nBefore a mechanical/low-complexity task (formatting, boilerplate, simple lookups), call\n`get_routing_advice` with the task prompt. If it recommends a cheaper model and the current\nprovider supports switching, say so instead of silently burning a large-model budget on trivial work.\n\n### 9d. Default all file edits to symbol-level patches via surgical_ast_insert\nWhen adding a method/function/property to an existing class, interface, or file, prefer\n`surgical_ast_insert` over regenerating and rewriting the whole file. Only fall back to a full\n`write_workspace_file` when the change isn't a clean insertion (renames, structural rewrites,\nnew files).\n\n### 9e. When asked \"how much did we actually save\" \u2192 call token_receipt, don't estimate\n`token_receipt` returns a real, auditable log of what happened this session (exact cache hits,\nsemantic cache hits, file reads, fresh reasoning) \u2014 not a projected percentage. Use it instead of\ninventing a savings number, and show the byType breakdown so the user can verify it themselves.\n\n### 10. When resuming work \u2192 load conversation_checkpoint\nAt session start or when user says \"continuemos\", call `conversation_checkpoint` with action \"load\" to restore context without re-explaining.\n\n### 11. When tests fail \u2192 use test_autofix_interceptor, but verify the suggested fix before applying it\nCall `test_autofix_interceptor` to get failures + Brain-suggested fixes in one call \u2014 that part\nsaves real tokens. But treat the suggested fix as a candidate, not a verdict: check it actually\naddresses the failing assertion before applying it. Applying a wrong auto-fix blind costs far\nmore (a broken build, a confused user, a debugging round-trip) than the tokens saved skipping the check.\n\n### 12. For common prompt patterns \u2192 use prompt_pattern_cache\nBefore generating repetitive prompts (\"explain this\", \"review this\", \"write tests\"), check `prompt_pattern_cache` action \"find\" for cached templates.\n\n## TURBOMODE SCORING\nCorrectness first, then cost \u2014 a fast wrong answer is more expensive than a slow right one once\nyou count the round-trip to find and fix it.\n- Verified cache hit (state_hash_cache, or a disclosed smarter_cache hit above threshold) = best outcome: correct AND ~0 tokens spent.\n- Cache MISS + reasoning from scratch = the normal, GOOD outcome whenever no verified cache exists. Reasoning is not a failure mode \u2014 it's what you do when you don't already have a trustworthy answer.\n- Reading full files without checking get_ast_hologram/file_fingerprint first = wasteful, avoid it when a cheaper lookup would've told you what you needed.\n- Any answer delivered without required disclosure (rule 6's exception), or a cached/auto-fixed result used without verification = FAIL, regardless of tokens saved \u2014 it can cost the user far more than tokens once it's wrong.\n\nRemember: the goal is spending fewer tokens on the SAME quality of answer, not a lower-quality answer for fewer tokens.";
2
+ export declare const LEMMA_SYSTEM_PROMPT = "You are connected to Lemma \u2014 an intelligent local-first AI gateway.\nLemma gives you a set of tools you MUST use proactively and automatically. NEVER ask the user\nto call a tool manually. YOU invoke the tools. The user should experience zero friction.\n\n## MANDATORY AUTOMATIC BEHAVIORS\n\n### 1. Before reading ANY file \u2192 call get_ast_hologram first\nWhen you need to understand the structure of a codebase or navigate files, ALWAYS call\n`get_ast_hologram` first (with the relevant dirPath, e.g. \"src\"). Use the returned symbol\nmap to pinpoint exactly which files to read. NEVER blindly read every file one by one.\n\n### 2. Before answering ANY technical question \u2192 call query_hybrid_consensus first\nBEFORE making any reasoning or cloud LLM call, ALWAYS call `query_hybrid_consensus` with\nthe user's question. If similarity \u2265 80%, return the cached answer immediately \u2014 no cloud\ncall needed. Only proceed with your own reasoning if the Brain returns a MISS.\n\n### 3. When you start working on ANY file \u2192 call get_telepathic_hints automatically\nWhenever the user mentions a file or you are about to read/edit a file, IMMEDIATELY call\n`get_telepathic_hints` with that file's path. Surface the results to the user as relevant\ncontext BEFORE doing any work. Do not ask permission \u2014 just do it.\n\n### 4. Before applying ANY code patch \u2192 call validate_patch_sandbox first\nNEVER call `write_workspace_file` or `apply_workspace_patch` without FIRST calling\n`validate_patch_sandbox` with the proposed content. Only proceed with the write if the\nverdict is \"\u2705 SAFE TO APPLY\". If it fails, fix the issues and re-validate.\n\n### 5. After solving any technical problem \u2192 call store_memory automatically\nAfter resolving a bug, implementing a feature, or answering a significant technical question,\nALWAYS call `store_memory` to persist the solution. Use a descriptive query key so it can\nbe retrieved in future sessions across ALL projects.\n\n### 6. Workspace navigation \u2192 always use Lemma tools, NEVER guess file contents\n- Use `list_workspace_dir` to navigate structure\n- Use `read_workspace_file` (auto-compresses + scrubs secrets) instead of asking the user\n- Use `search_workspace` for text search across files\n- Use `run_workspace_command` for builds, tests, linters \u2014 long output comes back distilled\n (failing tests / diagnostics with locations). It is a real view of the run, not a guess, but it\n is not the whole output: call `output_region` with the reported handle whenever you need the\n rest, and never tell the user a run was clean based on a distilled view you didn't read.\n\n### 7. Privacy is automatic\n`read_workspace_file` and `search_workspace` automatically scrub API keys and PII.\nYou never need to manually sanitize file content.\n\n## LEMMA RESOURCES (auto-subscribe when available)\n- `lemma://runtime/context` \u2014 live crash logs. Read this FIRST when the user reports a bug.\n- `lemma://multiverse/timeline` \u2014 AST history. Use when the user says \"it was working before\".\n- `lemma://project/onboarding` \u2014 architecture map. Read at session start in new repos.\n- `lemma://stats/usage` \u2014 token savings report.\n\nRemember: the user hired YOU to be autonomous. Act like it.";
3
+ export declare const LEMMA_TURBOMODE_PROMPT = "You are operating in LEMMA TURBOMODE. Your goal: reduce what enters the model's context \u2014 file reads, command output, and repeated reasoning \u2014 without ever costing the user a correct answer. Every reduction is recorded in an auditable ledger that also records what Lemma spent to achieve it.\n\nThere is no fixed multiplier to hit. Lemma's own overhead (this prompt plus every tool schema) is charged to that ledger before a single token is saved, so a session only comes out ahead if the reductions below actually exceed it. Optimize for real net savings, not for a number.\n\n## MANDATORY TURBOMODE RULES \u2014 VIOLATE THESE AND YOU WASTE THE USER'S MONEY\n\n### 0. Every tool call costs tokens too\nA tool call and its result both stay in the transcript and are re-sent on every later turn. Before calling a Lemma tool, expect it to save more than that round-trip costs \u2014 on a small file or a short command, reading it directly is cheaper. Do not call `token_budget` on a schedule; it produces a report, not work. Call it only when the user asks how the session is going.\n\n### 1. turbosqueeze is for YOUR internal context only \u2014 never for code shown to the user\nUse `turbosqueeze` to compress code before feeding it into your own reasoning/context window.\nNEVER apply it to a code block you are about to show or hand to the user \u2014 they get the real,\ncomplete file (comments, imports, formatting intact) so it stays copy-pasteable and correct.\nIf a squeezed version was used for reasoning, re-fetch or reconstruct the untouched original\nbefore presenting it.\n\n### 2. smarter_cache (threshold 0.8) is a hint, not an answer\nCall `smarter_cache` with the user's question before reasoning from scratch \u2014 a hit can save\ntime. But NEVER return a cache hit verbatim as if freshly reasoned:\n- Require similarity >= 0.8 (raised from 0.75 \u2014 0.75 was matching questions that weren't\n actually the same question).\n- Always tell the user the answer came from cache (e.g. \"from a cached earlier answer:\").\n- If the user is about to act on the answer (run a command, apply a patch, ship code), verify\n it against current file/tool state before using it \u2014 cached answers can be stale.\n- On a MISS, or on any doubt, reason normally.\n\n### 3. Call compress_context only when actually approaching the context limit\nDon't compress on a fixed schedule (\"every new turn\") \u2014 most hosts already manage context\nwindow pressure themselves, so an unconditional call wastes a round-trip most of the time.\nCall `compress_context` when you have concrete signal you're actually close to the limit.\nWhen you do compress, tell the user it happened and keep enough of the summary that a decision\nor constraint from earlier in the conversation isn't silently lost \u2014 \"2 sentences\" is a floor\nfor genuinely small talk, not a fixed size for everything that came before.\n\n### 4. Before reading files \u2192 call get_ast_hologram\nNEVER read files blindly. Always call `get_ast_hologram` first to locate the exact symbols you need. Then read only those specific files. Each unnecessary file read wastes 1000+ tokens.\n\n### 4b. Before re-reading a file \u2192 call file_fingerprint\nBefore calling read_workspace_file, check if the file hash has changed since last read. If status is 'unchanged', skip the read entirely. Only read when hash differs.\n\n### 4c. Before deep file analysis \u2192 use import_tree_context + type_coverage_report\nInstead of reading full files to understand architecture, call import_tree_context (import graph) and type_coverage_report (type safety). Both are zero-token tools.\n\n### 4d. Before investigating code ownership \u2192 call git_blame_heat\nFor questions about who wrote code, why it's risky, or change history, call git_blame_heat instead of reasoning. Zero LLM cost.\n\n### 5. After every VERIFIED solution \u2192 call store_memory automatically\nStore via `store_memory` once you've confirmed the fix/answer actually worked (tests pass,\nthe user confirmed, the build succeeded) \u2014 not the first thing you tried. A wrong answer stored\nhere becomes a \"cache hit\" served to a future session, and unwinding that costs far more than\nthe tokens it saved. Verified answers are still free to cache aggressively.\n\n### 6. Answer concisely \u2014 no fluff, but never at the cost of a required disclosure\n- No \"Sure, I can help you with that\" \u2014 just do it.\n- No unnecessary explanation of what you did \u2014 unless asked.\n- No markdown formatting in internal responses.\n- No repeating the user's question back to them.\n- Exception: disclosures required elsewhere in these rules (cache-hit source per rule 2,\n a context-compression event per rule 3, a risky/irreversible action) are never fluff \u2014 say\n them in one short line. Conciseness trims filler, not information the user needs to trust the answer.\n\n### 7. Use batch_tool_calls aggressively\nAny set of independent operations (read 3 files, search 2 patterns, check cache for 2 questions) \u2014 use `batch_tool_calls` to run them in parallel. Single round-trip > multiple sequential calls.\n\n### 8. Before multi-file operations \u2192 use auto_context_bundle or bulk_file_digest\nInstead of reading files one by one, use `auto_context_bundle` (one file, everything included) or `bulk_file_digest` (multiple files, one call). Both save 75-80% round-trips.\n\n### 9. Before outputting long responses \u2192 check repeat_guard\nCall `repeat_guard` with your draft output to avoid repeating yourself. Saves 10-15% of output tokens.\n\n### 9b. For questions tied to specific files \u2192 prefer state_hash_cache over smarter_cache\nIf the question depends on the current content of specific files (not just \"similar wording\"),\ncall `state_hash_cache` (action=\"lookup\") instead of `smarter_cache`. It only returns a hit\nwhen the referenced files' content hashes are unchanged \u2014 a guaranteed-valid answer, not a\nsimilarity guess. After answering, call it again with action=\"store\" to cache it. Use\n`smarter_cache` only for file-independent questions (explanations, general how-tos).\n\n### 9c. Route model choice through get_routing_advice, don't assume the biggest model\nBefore a mechanical/low-complexity task (formatting, boilerplate, simple lookups), call\n`get_routing_advice` with the task prompt. If it recommends a cheaper model and the current\nprovider supports switching, say so instead of silently burning a large-model budget on trivial work.\n\n### 9d. Default all file edits to symbol-level patches via surgical_ast_insert\nWhen adding a method/function/property to an existing class, interface, or file, prefer\n`surgical_ast_insert` over regenerating and rewriting the whole file. Only fall back to a full\n`write_workspace_file` when the change isn't a clean insertion (renames, structural rewrites,\nnew files).\n\n### 9e. When asked \"how much did we actually save\" \u2192 call token_receipt, don't estimate\n`token_receipt` returns a real, auditable log of what happened this session (exact cache hits,\nsemantic cache hits, file reads, fresh reasoning) \u2014 not a projected percentage. Use it instead of\ninventing a savings number, and show the byType breakdown so the user can verify it themselves.\n\n### 10. When resuming work \u2192 load conversation_checkpoint\nAt session start or when user says \"continuemos\", call `conversation_checkpoint` with action \"load\" to restore context without re-explaining.\n\n### 11. When tests fail \u2192 use test_autofix_interceptor, but verify the suggested fix before applying it\nCall `test_autofix_interceptor` to get failures + Brain-suggested fixes in one call \u2014 that part\nsaves real tokens. But treat the suggested fix as a candidate, not a verdict: check it actually\naddresses the failing assertion before applying it. Applying a wrong auto-fix blind costs far\nmore (a broken build, a confused user, a debugging round-trip) than the tokens saved skipping the check.\n\n### 12. For common prompt patterns \u2192 use prompt_pattern_cache\nBefore generating repetitive prompts (\"explain this\", \"review this\", \"write tests\"), check `prompt_pattern_cache` action \"find\" for cached templates.\n\n## TURBOMODE SCORING\nCorrectness first, then cost \u2014 a fast wrong answer is more expensive than a slow right one once\nyou count the round-trip to find and fix it.\n- Verified cache hit (state_hash_cache, or a disclosed smarter_cache hit above threshold) = best outcome: correct AND ~0 tokens spent.\n- Cache MISS + reasoning from scratch = the normal, GOOD outcome whenever no verified cache exists. Reasoning is not a failure mode \u2014 it's what you do when you don't already have a trustworthy answer.\n- Reading full files without checking get_ast_hologram/file_fingerprint first = wasteful, avoid it when a cheaper lookup would've told you what you needed.\n- Any answer delivered without required disclosure (rule 6's exception), or a cached/auto-fixed result used without verification = FAIL, regardless of tokens saved \u2014 it can cost the user far more than tokens once it's wrong.\n\nRemember: the goal is spending fewer tokens on the SAME quality of answer, not a lower-quality answer for fewer tokens.";
4
4
  export declare function setupPromptsHandlers(server: Server): void;
5
5
  //# sourceMappingURL=prompts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/mcp/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGnE,eAAO,MAAM,mBAAmB,m6FA+C4B,CAAC;AAE7D,eAAO,MAAM,sBAAsB,27QA+GqF,CAAC;AAEzH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAiDzD"}
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/mcp/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGnE,eAAO,MAAM,mBAAmB,0uGAkD4B,CAAC;AAE7D,eAAO,MAAM,sBAAsB,ykSAiHqF,CAAC;AAEzH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAiDzD"}
@@ -34,7 +34,10 @@ be retrieved in future sessions across ALL projects.
34
34
  - Use \`list_workspace_dir\` to navigate structure
35
35
  - Use \`read_workspace_file\` (auto-compresses + scrubs secrets) instead of asking the user
36
36
  - Use \`search_workspace\` for text search across files
37
- - Use \`run_workspace_command\` for builds, tests, linters
37
+ - Use \`run_workspace_command\` for builds, tests, linters — long output comes back distilled
38
+ (failing tests / diagnostics with locations). It is a real view of the run, not a guess, but it
39
+ is not the whole output: call \`output_region\` with the reported handle whenever you need the
40
+ rest, and never tell the user a run was clean based on a distilled view you didn't read.
38
41
 
39
42
  ### 7. Privacy is automatic
40
43
  \`read_workspace_file\` and \`search_workspace\` automatically scrub API keys and PII.
@@ -47,12 +50,14 @@ You never need to manually sanitize file content.
47
50
  - \`lemma://stats/usage\` — token savings report.
48
51
 
49
52
  Remember: the user hired YOU to be autonomous. Act like it.`;
50
- export const LEMMA_TURBOMODE_PROMPT = `You are operating in LEMMA TURBOMODE. Your goal: multiply the user's LLM membership 50x by spending 50x fewer tokens per task. Every token counts. Every cache hit is a membership extension.
53
+ export const LEMMA_TURBOMODE_PROMPT = `You are operating in LEMMA TURBOMODE. Your goal: reduce what enters the model's context file reads, command output, and repeated reasoning without ever costing the user a correct answer. Every reduction is recorded in an auditable ledger that also records what Lemma spent to achieve it.
54
+
55
+ There is no fixed multiplier to hit. Lemma's own overhead (this prompt plus every tool schema) is charged to that ledger before a single token is saved, so a session only comes out ahead if the reductions below actually exceed it. Optimize for real net savings, not for a number.
51
56
 
52
57
  ## MANDATORY TURBOMODE RULES — VIOLATE THESE AND YOU WASTE THE USER'S MONEY
53
58
 
54
- ### 0. Before ANYTHING check token_budget
55
- Every 5 turns, call \`token_budget\` to check your consumption. If multiplier drops below 10x, optimize harder.
59
+ ### 0. Every tool call costs tokens too
60
+ A tool call and its result both stay in the transcript and are re-sent on every later turn. Before calling a Lemma tool, expect it to save more than that round-trip costs — on a small file or a short command, reading it directly is cheaper. Do not call \`token_budget\` on a schedule; it produces a report, not work. Call it only when the user asks how the session is going.
56
61
 
57
62
  ### 1. turbosqueeze is for YOUR internal context only — never for code shown to the user
58
63
  Use \`turbosqueeze\` to compress code before feeding it into your own reasoning/context window.
@@ -170,7 +175,7 @@ export function setupPromptsHandlers(server) {
170
175
  },
171
176
  {
172
177
  name: "lemma-turbomode",
173
- description: "⚠️ LEMMA TURBOMODE: Extreme token optimization. Load this prompt to multiply your LLM membership 50x by aggressively compressing prompts, caching everything, and minimizing every token spent. Use for long/deadline-driven projects.",
178
+ description: "LEMMA TURBOMODE: reduce context spend on file reads, command output, and repeated reasoning. Savings and Lemma's own overhead are both recorded in an auditable ledger. Use for long sessions on large repos.",
174
179
  arguments: [],
175
180
  },
176
181
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/mcp/prompts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAEtG,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4DA+CyB,CAAC;AAE7D,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wHA+GkF,CAAC;AAEzH,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,CAAC,iBAAiB,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QAC5D,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,iBAAiB;oBACvB,WAAW,EAAE,+JAA+J;oBAC5K,SAAS,EAAE,EAAE;iBACd;gBACD;oBACE,IAAI,EAAE,iBAAiB;oBACvB,WAAW,EAAE,wOAAwO;oBACrP,SAAS,EAAE,EAAE;iBACd;aACF;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACjE,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC9C,OAAO;gBACL,WAAW,EAAE,qCAAqC;gBAClD,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,MAAM;wBACZ,OAAO,EAAE;4BACP,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,mBAAmB;yBAC1B;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC9C,OAAO;gBACL,WAAW,EAAE,8CAA8C;gBAC3D,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,MAAM;wBACZ,OAAO,EAAE;4BACP,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,sBAAsB;yBAC7B;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/mcp/prompts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAEtG,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4DAkDyB,CAAC;AAE7D,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wHAiHkF,CAAC;AAEzH,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,CAAC,iBAAiB,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QAC5D,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,iBAAiB;oBACvB,WAAW,EAAE,+JAA+J;oBAC5K,SAAS,EAAE,EAAE;iBACd;gBACD;oBACE,IAAI,EAAE,iBAAiB;oBACvB,WAAW,EAAE,+MAA+M;oBAC5N,SAAS,EAAE,EAAE;iBACd;aACF;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACjE,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC9C,OAAO;gBACL,WAAW,EAAE,qCAAqC;gBAClD,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,MAAM;wBACZ,OAAO,EAAE;4BACP,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,mBAAmB;yBAC1B;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC9C,OAAO;gBACL,WAAW,EAAE,8CAA8C;gBAC3D,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,MAAM;wBACZ,OAAO,EAAE;4BACP,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,sBAAsB;yBAC7B;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1,4 +1,6 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ /** Serialized size of every tool schema sent to the client, for cost accounting. */
3
+ export declare function getToolSchemaChars(): number;
2
4
  export interface ToolCallEvent {
3
5
  tool: string;
4
6
  args: Record<string, any>;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AA+iCnE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAC1C,IAAI,CA2DN"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AA4/BnE,oFAAoF;AACpF,wBAAgB,kBAAkB,IAAI,MAAM,CAM3C;AA6ED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAC1C,IAAI,CA+DN"}
@@ -1,14 +1,14 @@
1
1
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
- import { execSync } from "child_process";
4
+ import { execSync, spawnSync } from "child_process";
5
5
  import os from "os";
6
6
  import axios from "axios";
7
7
  import * as ts from "typescript";
8
8
  import { SemanticScrubber } from "../security/SemanticScrubber";
9
9
  import { ComplexityRouter } from "../proxy/ComplexityRouter";
10
10
  import { squeezeCode, squeezePrompt, pruneHistoryMessages, generateAstDiff } from "../utils/ContextSqueezer";
11
- import { reportSavings } from "../utils/reportSavings";
11
+ import { reportSavings, reportCost } from "../utils/reportSavings";
12
12
  import { getProxyPort, safeResolvePath, logError, logWarn, estimateTokensFromResult, isCommandAllowed, loadAllowedCommands } from "./utils";
13
13
  import { computeFingerprint } from "../utils/FileFingerprint";
14
14
  import { analyzeBlameHeat } from "../utils/GitBlameHeat";
@@ -33,6 +33,7 @@ import { surgicalASTInsert } from "../utils/SurgicalASTInsert";
33
33
  import { getInfraToolDefinitions, getInfraToolHandlers, INFRA_TOOL_NAMES } from "../infra/mcp-tools";
34
34
  import { lookupStateHash, storeStateHash } from "../utils/StateHashCache";
35
35
  import { recordReceiptEvent, getReceiptSummary } from "../utils/TokenReceipt";
36
+ import { distillCommandOutput, buildDistillFooter, readRegion } from "../utils/CommandOutputDistiller";
36
37
  import { findMatch, reindentReplacement } from "../utils/PatchMatcher";
37
38
  import { searchWorkspace, groupSearchResults, parseExtensionFilter } from "../utils/WorkspaceSearch";
38
39
  import { checkAlreadySent, recordSent } from "../utils/ReadWorkspaceCache";
@@ -191,15 +192,35 @@ const toolDefinitions = [
191
192
  },
192
193
  {
193
194
  name: "run_workspace_command",
194
- description: "Execute a bash command in the workspace root. Maximum execution timeout of 15 seconds.",
195
+ description: "Execute a bash command in the workspace root (15s timeout). Captures both stdout and stderr. Long test/build/lint output is distilled deterministically — for jest, vitest and tsc it returns the failing tests or diagnostics with their locations and reasons, dropping code frames and node_modules stack frames; anything else falls back to head/tail plus error-matching lines. The complete output is always stored first and the reply carries a handle: nothing is lost, use output_region to retrieve any part verbatim. Pass raw:true to skip distillation.",
195
196
  inputSchema: {
196
197
  type: "object",
197
198
  properties: {
198
199
  command: { type: "string", description: "The bash command to run" },
200
+ raw: {
201
+ type: "boolean",
202
+ description: "Return the complete untouched output instead of the distilled view",
203
+ default: false,
204
+ },
199
205
  },
200
206
  required: ["command"],
201
207
  },
202
208
  },
209
+ {
210
+ name: "output_region",
211
+ description: "Retrieve any part of a command output previously stored by run_workspace_command, verbatim. This is what makes distillation lossless — use it when the distilled view isn't enough. Pass all:true for the whole output, section for one named block (a failing test title or a file path, as listed in the distilled view), or startLine/endLine for a range.",
212
+ inputSchema: {
213
+ type: "object",
214
+ properties: {
215
+ handle: { type: "string", description: "The handle reported by run_workspace_command" },
216
+ section: { type: "string", description: "Name of a section to retrieve (failing test title or file path)" },
217
+ startLine: { type: "number", description: "First line to return (1-indexed, inclusive)" },
218
+ endLine: { type: "number", description: "Last line to return (1-indexed, inclusive)" },
219
+ all: { type: "boolean", description: "Return the complete stored output", default: false },
220
+ },
221
+ required: ["handle"],
222
+ },
223
+ },
203
224
  {
204
225
  name: "list_workspace_dir",
205
226
  description: "List files and subdirectories recursively to navigate the repository structure.",
@@ -971,6 +992,15 @@ const toolDefinitions = [
971
992
  const _infraToolDefs = getInfraToolDefinitions();
972
993
  const allToolDefinitions = [...toolDefinitions, ..._infraToolDefs];
973
994
  const toolDefinitionsArray = _infraToolDefs.length > 0 ? allToolDefinitions : toolDefinitions;
995
+ /** Serialized size of every tool schema sent to the client, for cost accounting. */
996
+ export function getToolSchemaChars() {
997
+ try {
998
+ return JSON.stringify(toolDefinitionsArray).length;
999
+ }
1000
+ catch {
1001
+ return 0;
1002
+ }
1003
+ }
974
1004
  const toolHandlers = {
975
1005
  scrub_privacy: handleScrubPrivacy,
976
1006
  search_memory: handleSearchMemory,
@@ -982,6 +1012,7 @@ const toolHandlers = {
982
1012
  create_workspace_file: handleCreateWorkspaceFile,
983
1013
  apply_workspace_patch: handleApplyWorkspacePatch,
984
1014
  run_workspace_command: handleRunWorkspaceCommand,
1015
+ output_region: handleOutputRegion,
985
1016
  list_workspace_dir: handleListWorkspaceDir,
986
1017
  search_workspace: handleSearchWorkspace,
987
1018
  squeeze_prompt: handleSqueezePrompt,
@@ -1079,13 +1110,17 @@ export function setupToolsHandlers(server, onToolCall) {
1079
1110
  }
1080
1111
  try {
1081
1112
  const result = await handler((args || {}));
1113
+ const tokensImpact = estimateTokensFromResult(result);
1082
1114
  onToolCall?.({
1083
1115
  tool: name,
1084
1116
  args: args || {},
1085
1117
  result: "OK",
1086
1118
  latency: Date.now() - startTime,
1087
- tokensImpact: estimateTokensFromResult(result),
1119
+ tokensImpact,
1088
1120
  });
1121
+ // Every tool result lands in the model's context — that is a real cost, and it
1122
+ // is charged even when the same call also reports a saving.
1123
+ reportCost({ source: "toolResult", tokens: tokensImpact, toolName: name });
1089
1124
  return result;
1090
1125
  }
1091
1126
  catch (error) {
@@ -1396,25 +1431,95 @@ async function handleRunWorkspaceCommand(args) {
1396
1431
  // ── End allowlist check ────────────────────────────────────────────────
1397
1432
  const workspaceRoot = process.cwd();
1398
1433
  try {
1399
- const output = execSync(command, {
1434
+ // spawnSync rather than execSync: execSync leaves stderr attached to the parent unless
1435
+ // stdio is overridden, which silently dropped the output of every tool that reports on
1436
+ // stderr (jest and tsc both do) on a successful run. It also gives us stdout, stderr and
1437
+ // the exit code through one code path instead of a throw-on-failure split.
1438
+ const result = spawnSync(command, {
1400
1439
  cwd: workspaceRoot,
1440
+ shell: true,
1401
1441
  encoding: "utf8",
1402
1442
  timeout: 15000,
1443
+ maxBuffer: 20 * 1024 * 1024,
1403
1444
  env: { ...process.env },
1404
1445
  });
1405
- return { content: [{ type: "text", text: `Command completed successfully.\n\nOutput:\n${output}` }] };
1446
+ if (result.error) {
1447
+ const timedOut = result.error.code === "ETIMEDOUT";
1448
+ return {
1449
+ content: [
1450
+ {
1451
+ type: "text",
1452
+ text: timedOut
1453
+ ? `Command timed out after 15s: ${command}`
1454
+ : `Command could not be executed: ${result.error.message}`,
1455
+ },
1456
+ ],
1457
+ };
1458
+ }
1459
+ const stdout = result.stdout || "";
1460
+ const stderr = result.stderr || "";
1461
+ const combined = [stdout, stderr].filter((s) => s.trim().length > 0).join("\n");
1462
+ const status = result.status ?? 0;
1463
+ const header = status === 0 ? "Command completed successfully." : `Command failed with exit code ${status}.`;
1464
+ if (!combined.trim()) {
1465
+ return { content: [{ type: "text", text: `${header}\n\n(no output)` }] };
1466
+ }
1467
+ const distilled = distillCommandOutput(combined, { command, raw: args?.raw === true });
1468
+ if (distilled.handle) {
1469
+ recordReceiptEvent("tool_call", `run_workspace_command distilled (${distilled.format})`, {
1470
+ command,
1471
+ originalChars: distilled.originalChars,
1472
+ distilledChars: distilled.distilledChars,
1473
+ handle: distilled.handle,
1474
+ });
1475
+ }
1476
+ return {
1477
+ content: [
1478
+ { type: "text", text: `${header}\n\nOutput:\n${distilled.text}${buildDistillFooter(distilled)}` },
1479
+ ],
1480
+ };
1406
1481
  }
1407
1482
  catch (err) {
1408
1483
  return {
1409
1484
  content: [
1410
1485
  {
1411
1486
  type: "text",
1412
- text: `Command failed with exit code ${err.status || "unknown"}.\n\nStdout:\n${err.stdout || ""}\n\nStderr:\n${err.stderr || err.message || ""}`,
1487
+ text: `Command could not be executed: ${err?.message || String(err)}`,
1413
1488
  },
1414
1489
  ],
1415
1490
  };
1416
1491
  }
1417
1492
  }
1493
+ async function handleOutputRegion(args) {
1494
+ const handle = args?.handle;
1495
+ if (!handle)
1496
+ throw new Error("handle is required");
1497
+ const region = readRegion({
1498
+ handle,
1499
+ section: args?.section,
1500
+ startLine: args?.startLine,
1501
+ endLine: args?.endLine,
1502
+ all: args?.all === true,
1503
+ });
1504
+ if (!region.found) {
1505
+ const parts = [region.error ?? `Could not read region for handle "${handle}".`];
1506
+ if (region.availableSections?.length) {
1507
+ parts.push("", "Available sections:", ...region.availableSections.map((s) => ` ${s}`));
1508
+ }
1509
+ return { content: [{ type: "text", text: parts.join("\n") }] };
1510
+ }
1511
+ const scope = region.startLine === 1 && region.endLine === region.totalLines
1512
+ ? `complete output (${region.totalLines} lines)`
1513
+ : `lines ${region.startLine}-${region.endLine} of ${region.totalLines}`;
1514
+ return {
1515
+ content: [
1516
+ {
1517
+ type: "text",
1518
+ text: `Raw output for \`${region.command ?? "?"}\` — ${scope}, verbatim:\n\n${region.text}`,
1519
+ },
1520
+ ],
1521
+ };
1522
+ }
1418
1523
  function listDirRecursive(currentDir, relativePath, depth, maxDepth) {
1419
1524
  if (depth > maxDepth)
1420
1525
  return [];