pi-lean-ctx 3.5.24 → 3.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.
package/README.md CHANGED
@@ -1,34 +1,36 @@
1
1
  # pi-lean-ctx
2
2
 
3
- CLI-first [Pi Coding Agent](https://github.com/badlogic/pi-mono) extension that routes Pi’s built-in tools through [lean-ctx](https://leanctx.com) for **60–90% token savings**.
3
+ [Pi Coding Agent](https://github.com/badlogic/pi-mono) extension that provides `ctx_`-prefixed tools backed by [lean-ctx](https://leanctx.com) for **60–90% token savings**.
4
4
 
5
5
  - **Default**: CLI-only (no MCP required)
6
6
  - **Optional**: enable MCP tools (`LEAN_CTX_PI_ENABLE_MCP=1`) or run `lean-ctx init --agent pi --mode mcp`
7
7
 
8
8
  ## What it does
9
9
 
10
- ### Built-in Tool Overrides (CLI)
10
+ ### ctx_ Tools (CLI-backed)
11
11
 
12
- Overrides Pi's built-in tools to route them through `lean-ctx`:
12
+ Replaces Pi's built-in `read`, `bash`, `ls`, `find`, `grep` with `ctx_`-prefixed versions:
13
13
 
14
- | Tool | Compression |
15
- |------|------------|
16
- | `bash` | All shell commands compressed via lean-ctx's 95+ patterns |
17
- | `read` | Smart mode selection (full/map/signatures) based on file type and size |
18
- | `grep` | Results grouped and compressed via ripgrep + lean-ctx |
19
- | `find` | File listings compressed and .gitignore-aware |
20
- | `ls` | Directory output compressed |
14
+ | Tool | Replaces | Compression |
15
+ |------|----------|-------------|
16
+ | `ctx_read` | `read` | Smart mode selection (full/map/signatures) based on file type and size |
17
+ | `ctx_shell` | `bash` | All shell commands compressed via lean-ctx's 95+ patterns |
18
+ | `ctx_grep` | `grep` | Results grouped and compressed via ripgrep + lean-ctx |
19
+ | `ctx_find` | `find` | File listings compressed and .gitignore-aware |
20
+ | `ctx_ls` | `ls` | Directory output compressed |
21
+
22
+ Pi's `edit` and `write` builtins remain unchanged.
21
23
 
22
24
  ### Direct lean-ctx CLI tool
23
25
 
24
- The extension registers a `lean_ctx` tool that runs `lean-ctx` directly (no nested compression).
26
+ The `lean_ctx` tool runs `lean-ctx` directly (no nested compression).
25
27
  Use it for commands like:
26
28
 
27
- - `lean-ctx overview`
28
- - `lean-ctx session …`
29
- - `lean-ctx knowledge …`
30
- - `lean-ctx gain` / `lean-ctx stats`
31
- - `lean-ctx index …`
29
+ - `lean_ctx overview`
30
+ - `lean_ctx session …`
31
+ - `lean_ctx knowledge …`
32
+ - `lean_ctx gain` / `lean_ctx stats`
33
+ - `lean_ctx index …`
32
34
 
33
35
  ### Optional MCP Tools (Embedded Bridge)
34
36
 
@@ -43,24 +45,11 @@ server and registers advanced tools directly in Pi:
43
45
  | `ctx_overview` | Codebase overview and architecture analysis |
44
46
  | `ctx_compress` | Manual compression control |
45
47
  | `ctx_metrics` | Token savings dashboard |
46
- | `ctx_agent` | Multi-agent coordination and handoffs |
47
- | `ctx_graph` | Dependency graph analysis |
48
- | `ctx_discover` | Smart code discovery |
49
- | `ctx_context` | Context window management |
50
- | `ctx_preload` | Predictive file preloading |
51
- | `ctx_delta` | Changed-lines-only reads |
52
- | `ctx_edit` | Read-modify-write in one call |
53
- | `ctx_dedup` | Duplicate context elimination |
54
- | `ctx_fill` | Template completion |
55
- | `ctx_intent` | Intent-based task routing |
56
- | `ctx_response` | Response optimization |
57
- | `ctx_wrapped` | Wrapped command execution |
58
- | `ctx_benchmark` | Compression benchmarking |
59
- | `ctx_analyze` | Code analysis |
60
- | `ctx_cache` | Cache management |
61
- | `ctx_execute` | Direct command execution |
62
-
63
- If you don’t want MCP: keep it disabled and use the CLI overrides + `lean_ctx` tool only.
48
+ | `ctx_multi_read` | Batch file reads |
49
+ | `ctx_search` | MCP-native search |
50
+ | `ctx_tree` | File tree listing |
51
+
52
+ If you don't want MCP: keep it disabled and use the `ctx_` CLI tools + `lean_ctx` tool only.
64
53
 
65
54
  ## Install
66
55
 
@@ -83,14 +72,17 @@ lean-ctx init --agent pi
83
72
 
84
73
  ## How it works
85
74
 
86
- ### CLI overrides (bash, read, grep, find, ls)
75
+ ### ctx_ tools (CLI-backed)
87
76
 
88
- These tools invoke the `lean-ctx` binary via CLI with `LEAN_CTX_COMPRESS=1`. The output is parsed for compression stats and displayed with a token savings footer.
77
+ These tools invoke the `lean-ctx` binary via CLI with `LEAN_CTX_COMPRESS=1`.
78
+ The built-in tools they replace (`read`, `bash`, `ls`, `find`, `grep`) are disabled
79
+ via `pi.setActiveTools()` so only the `ctx_` versions are available to the LLM.
89
80
 
90
81
  ### Optional MCP bridge (all other tools)
91
82
 
92
83
  If you enable the MCP bridge, pi-lean-ctx spawns the `lean-ctx` binary as an MCP server (JSON-RPC over stdio).
93
- It discovers available tools via `list_tools`, filters out those already covered by CLI overrides, and registers the rest as native Pi tools.
84
+ It discovers available tools via `list_tools`, filters out those already covered by `ctx_` CLI tools,
85
+ and registers the rest as native Pi tools.
94
86
 
95
87
  If `lean-ctx` is already configured as an MCP server via [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter) in `~/.pi/agent/mcp.json`, the embedded bridge is skipped to avoid duplicate tools.
96
88
 
@@ -150,7 +142,7 @@ The extension locates the `lean-ctx` binary in this order:
150
142
 
151
143
  ## Smart Read Modes
152
144
 
153
- The `read` tool automatically selects the optimal lean-ctx mode:
145
+ The `ctx_read` tool automatically selects the optimal lean-ctx mode:
154
146
 
155
147
  | File Type | Size | Mode |
156
148
  |-----------|------|------|
@@ -166,7 +158,7 @@ The `read` tool automatically selects the optimal lean-ctx mode:
166
158
  Use `/lean-ctx` in Pi to check:
167
159
  - Which binary is being used
168
160
  - MCP bridge status (disabled / embedded / adapter)
169
- - Number and names of registered MCP tools
161
+ - Active `ctx_` tool names
170
162
 
171
163
  ## Disabling specific tools
172
164
 
@@ -2,7 +2,6 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
2
  import {
3
3
  createBashToolDefinition,
4
4
  createReadToolDefinition,
5
- DEFAULT_MAX_BYTES,
6
5
  DEFAULT_MAX_LINES,
7
6
  getLanguageFromPath,
8
7
  highlightCode,
@@ -36,10 +35,22 @@ const IMAGE_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
36
35
  const CODE_FULL_READ_MAX_BYTES = 8 * 1024;
37
36
  const CODE_SIGNATURES_MIN_BYTES = 96 * 1024;
38
37
 
38
+ // Pi builtins we replace with ctx_ prefixed versions
39
+ const DISABLED_BUILTIN_TOOLS = new Set(["read", "bash", "ls", "find", "grep"]);
40
+ // Max bytes constant for truncation warnings (same as Pi's DEFAULT_MAX_BYTES)
41
+ const DEFAULT_MAX_BYTES = 8192;
42
+
43
+ const readModeSchema = Type.Union([
44
+ Type.Literal("full"),
45
+ Type.Literal("map"),
46
+ Type.Literal("signatures"),
47
+ ], { description: "Override auto-selection: full (complete content), map (deps+API signatures), signatures (AST only)" });
48
+
39
49
  const readSchema = Type.Object({
40
50
  path: Type.String({ description: "Path to the file to read (relative or absolute)" }),
41
51
  offset: Type.Optional(Type.Number({ description: "Line number to start reading from (1-indexed)" })),
42
52
  limit: Type.Optional(Type.Number({ description: "Maximum number of lines to read" })),
53
+ mode: Type.Optional(readModeSchema),
43
54
  });
44
55
 
45
56
  const lsSchema = Type.Object({
@@ -276,6 +287,13 @@ async function execLeanCtx(pi: ExtensionAPI, args: string[]) {
276
287
  }
277
288
 
278
289
  export default async function (pi: ExtensionAPI) {
290
+ // Defer setActiveTools to session_start — runtime actions aren't available during extension load
291
+ // Must run on every session_start since active tools are per-session
292
+ pi.on("session_start", () => {
293
+ const activeTools = pi.getActiveTools().filter((name) => !DISABLED_BUILTIN_TOOLS.has(name));
294
+ pi.setActiveTools(activeTools);
295
+ });
296
+
279
297
  const baseBashTool = createBashToolDefinition(process.cwd(), {
280
298
  spawnHook: ({ command, cwd, env }) => {
281
299
  const bin = resolveBinary();
@@ -295,19 +313,21 @@ export default async function (pi: ExtensionAPI) {
295
313
  raw: Type.Optional(Type.Boolean({ description: "Skip compression, return full uncompressed output" })),
296
314
  });
297
315
 
316
+ // ── ctx_shell (replaces bash) ─────────────────────────────────────────
298
317
  pi.registerTool({
299
- ...baseBashTool,
300
- parameters: bashSchemaWithRaw,
318
+ name: "ctx_shell",
319
+ label: "ctx_shell",
301
320
  description:
302
- "Execute a bash command. Output is auto-compressed by lean-ctx. "
303
- + "IMPORTANT: Do NOT use bash to read files (cat/head/tail) — use the read tool instead. "
304
- + "Do NOT use bash for grep/find/ls — use the dedicated tools. "
321
+ "Execute a shell command. Output is auto-compressed by lean-ctx. "
322
+ + "IMPORTANT: Do NOT use ctx_shell to read files (cat/head/tail) — use ctx_read instead. "
323
+ + "Do NOT use ctx_shell for grep/find/ls — use ctx_grep, ctx_find, ctx_ls. "
305
324
  + "Set raw=true to skip compression when exact output matters. "
306
325
  + "Use timeout (seconds) to prevent hanging commands.",
307
- promptSnippet: "Run shell commands (not for file reading — use read tool)",
326
+ promptSnippet: "Run shell commands (not for file reading — use ctx_read)",
308
327
  promptGuidelines: [
309
- "Use bash only for commands with side effects: build, test, install, git, run scripts.",
328
+ "Use ctx_shell only for commands with side effects: build, test, install, git, run scripts.",
310
329
  ],
330
+ parameters: bashSchemaWithRaw,
311
331
  async execute(toolCallId, params, signal, onUpdate, ctx) {
312
332
  const isRaw = !!params.raw;
313
333
  const toolParams = { command: params.command, timeout: params.timeout };
@@ -335,19 +355,22 @@ export default async function (pi: ExtensionAPI) {
335
355
  },
336
356
  });
337
357
 
358
+ // ── ctx_read (replaces read) ──────────────────────────────────────────
338
359
  const nativeReadTool = createReadToolDefinition(process.cwd());
339
360
 
340
361
  pi.registerTool({
341
- name: "read",
342
- label: "Read",
362
+ name: "ctx_read",
363
+ label: "ctx_read",
343
364
  description:
344
- "Read file contents. ALWAYS use this instead of cat/head/tail via bash. "
365
+ "Read file contents. ALWAYS use ctx_read instead of cat/head/tail via ctx_shell. "
345
366
  + "Auto-selects mode: configs (.yaml/.json/.toml/.env) are always full-read. "
346
367
  + "Code files: full (<8KB), map (8-96KB), signatures (>96KB). "
368
+ + "Add mode=full to get complete file content (bypasses cache). "
347
369
  + "Use offset and limit to read specific line ranges.",
348
370
  promptSnippet: "Read file contents (always use instead of cat)",
349
371
  promptGuidelines: [
350
- "Use read to inspect file contents instead of cat or less.",
372
+ "Use ctx_read to inspect file contents instead of cat or less.",
373
+ "Use mode=full if you need the complete file content.",
351
374
  ],
352
375
  parameters: readSchema,
353
376
  renderCall(args, theme, context) {
@@ -386,11 +409,11 @@ export default async function (pi: ExtensionAPI) {
386
409
  | undefined;
387
410
  if (truncation?.truncated) {
388
411
  if (truncation.firstLineExceedsLimit) {
389
- text += `\n${theme.fg("warning", `[First line exceeds ${Math.round((truncation.maxBytes ?? DEFAULT_MAX_BYTES) / 1024)}KB limit]`)}`;
412
+ text += `\n${theme.fg("warning", `[First line exceeds ${Math.round((truncation.maxBytes ?? 8192) / 1024)}KB limit]`)}`;
390
413
  } else if (truncation.truncatedBy === "lines") {
391
414
  text += `\n${theme.fg("warning", `[Truncated: ${truncation.outputLines} of ${truncation.totalLines} lines]`)}`;
392
415
  } else {
393
- text += `\n${theme.fg("warning", `[Truncated: ${truncation.outputLines} lines (${Math.round((truncation.maxBytes ?? DEFAULT_MAX_BYTES) / 1024)}KB limit)]`)}`;
416
+ text += `\n${theme.fg("warning", `[Truncated: ${truncation.outputLines} lines (${Math.round((truncation.maxBytes ?? 8192) / 1024)}KB limit)]`)}`;
394
417
  }
395
418
  }
396
419
 
@@ -431,8 +454,9 @@ export default async function (pi: ExtensionAPI) {
431
454
  return nativeReadTool.execute(_toolCallId, { ...params, path: absolutePath }, signal, onUpdate, ctx);
432
455
  }
433
456
 
434
- const mode = await chooseReadMode(absolutePath);
435
- const args = mode === "full" ? ["read", absolutePath] : ["read", absolutePath, "-m", mode];
457
+ const isExplicitFull = params.mode === "full";
458
+ const mode = params.mode ?? await chooseReadMode(absolutePath);
459
+ const args = ["read", absolutePath, "-m", mode, ...(isExplicitFull ? ["--fresh"] : [])];
436
460
  const output = await execLeanCtx(pi, args);
437
461
  const originalText = await readFile(absolutePath, "utf8");
438
462
  const decorated = withFooter(output, { originalText, always: true, preferEstimate: true });
@@ -444,9 +468,10 @@ export default async function (pi: ExtensionAPI) {
444
468
  },
445
469
  });
446
470
 
471
+ // ── ctx_ls (replaces ls) ──────────────────────────────────────────────
447
472
  pi.registerTool({
448
- name: "ls",
449
- label: "ls",
473
+ name: "ctx_ls",
474
+ label: "ctx_ls",
450
475
  description: "List directory contents. Use limit to reduce output size.",
451
476
  promptSnippet: "List directory contents",
452
477
  parameters: lsSchema,
@@ -462,9 +487,10 @@ export default async function (pi: ExtensionAPI) {
462
487
  },
463
488
  });
464
489
 
490
+ // ── ctx_find (replaces find) ──────────────────────────────────────────
465
491
  pi.registerTool({
466
- name: "find",
467
- label: "find",
492
+ name: "ctx_find",
493
+ label: "ctx_find",
468
494
  description: "Find files by glob pattern (respects .gitignore). Use limit to reduce output size.",
469
495
  promptSnippet: "Find files by glob pattern",
470
496
  parameters: findSchema,
@@ -480,9 +506,10 @@ export default async function (pi: ExtensionAPI) {
480
506
  },
481
507
  });
482
508
 
509
+ // ── ctx_grep (replaces grep) ──────────────────────────────────────────
483
510
  pi.registerTool({
484
- name: "grep",
485
- label: "grep",
511
+ name: "ctx_grep",
512
+ label: "ctx_grep",
486
513
  description: "Search file contents with ripgrep. Use limit to cap matches and context for surrounding lines.",
487
514
  promptSnippet: "Search file contents for patterns",
488
515
  parameters: grepSchema,
@@ -512,6 +539,7 @@ export default async function (pi: ExtensionAPI) {
512
539
  },
513
540
  });
514
541
 
542
+ // ── lean_ctx (CLI passthrough) ────────────────────────────────────────
515
543
  pi.registerTool({
516
544
  name: "lean_ctx",
517
545
  label: "lean_ctx",
@@ -575,6 +603,12 @@ export default async function (pi: ExtensionAPI) {
575
603
  }
576
604
  }
577
605
 
606
+ // Show active ctx_ tools
607
+ const ctxTools = pi.getActiveTools().filter((n) => n.startsWith("ctx_") || n === "lean_ctx");
608
+ if (ctxTools.length > 0) {
609
+ lines.push(`Active tools: ${ctxTools.join(", ")}`);
610
+ }
611
+
578
612
  const ok = found && (adapterConfigured || !enableMcpBridge || (status?.connected ?? false));
579
613
  ctx.ui.notify(lines.join("\n"), ok ? "info" : "warning");
580
614
  },
@@ -12,6 +12,8 @@ const CLI_OVERRIDE_TOOLS = new Set([
12
12
  "ctx_tree",
13
13
  ]);
14
14
 
15
+ // No additional prefix filter — CLI_OVERRIDE_TOOLS covers exactly the tools we overwrite
16
+
15
17
  const MAX_RECONNECT_ATTEMPTS = 3;
16
18
  const RECONNECT_DELAY_MS = 2000;
17
19
  const TOOL_CALL_TIMEOUT_MS = 120000;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-lean-ctx",
3
- "version": "3.5.24",
4
- "description": "Pi Coding Agent extension (CLI-first) \u2014 routes bash/read/grep/find/ls through lean-ctx CLI for strong token savings. Optional MCP bridge can register advanced tools.",
3
+ "version": "3.6.0",
4
+ "description": "Pi Coding Agent extension (CLI-first) routes bash/read/grep/find/ls through lean-ctx CLI for strong token savings. Optional MCP bridge can register advanced tools.",
5
5
  "keywords": [
6
6
  "pi-package",
7
7
  "lean-ctx",
@@ -36,5 +36,8 @@
36
36
  "files": [
37
37
  "extensions/",
38
38
  "README.md"
39
- ]
39
+ ],
40
+ "devDependencies": {
41
+ "typescript": "^6.0.3"
42
+ }
40
43
  }