pi-lean-ctx 3.8.5 → 3.8.7

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.
@@ -15,6 +15,15 @@ import { resolve } from "node:path";
15
15
  export interface PiLeanCtxFileConfig {
16
16
  /** Tool exposure: "additive" (Pi builtins + ctx_*) or "replace" (ctx_* only). */
17
17
  mode?: string;
18
+ /**
19
+ * Suppress only the native `bash` builtin (keep the other Pi builtins) so all
20
+ * shell runs through `ctx_shell`. In `additive` mode both `bash` and
21
+ * `ctx_shell` are active and agents tend to pick the uncompressed native
22
+ * `bash` (the R1 finding: 102 bash / 0 ctx_shell), so build/test output never
23
+ * gets compressed or metered. Default `false`; `replace` mode already implies
24
+ * it. Equivalent to `LEAN_CTX_PI_ROUTE_SHELL=1`.
25
+ */
26
+ routeShell?: boolean;
18
27
  /**
19
28
  * Start the embedded MCP bridge (the persistent session cache). Default
20
29
  * `true`; set `false` (or `LEAN_CTX_PI_ENABLE_MCP=0`) to force the one-shot
@@ -53,6 +62,8 @@ export type PiMode = "additive" | "replace";
53
62
  /** Fully resolved configuration after merging file, env vars and defaults. */
54
63
  export interface ResolvedPiConfig {
55
64
  mode: PiMode;
65
+ /** Force shell through `ctx_shell` by suppressing the native `bash` builtin. */
66
+ routeShell: boolean;
56
67
  enableMcp: boolean;
57
68
  /** Binary path from the file; `LEAN_CTX_BIN` still takes precedence at use time. */
58
69
  binaryOverride?: string;
@@ -107,6 +118,20 @@ function resolveMode(fileMode: string | undefined): PiMode {
107
118
  return raw === "replace" ? "replace" : "additive";
108
119
  }
109
120
 
121
+ /**
122
+ * Whether the native `bash` builtin should be suppressed so shell runs through
123
+ * `ctx_shell`. `replace` mode already hides every builtin, so it implies this;
124
+ * otherwise the env var wins over the file flag, defaulting off (non-regressive
125
+ * — `additive` users keep native `bash` unless they opt in).
126
+ */
127
+ export function resolveRouteShell(mode: PiMode, fileRouteShell: unknown): boolean {
128
+ if (mode === "replace") return true;
129
+ if (process.env.LEAN_CTX_PI_ROUTE_SHELL !== undefined) {
130
+ return envFlag("LEAN_CTX_PI_ROUTE_SHELL");
131
+ }
132
+ return fileRouteShell === true;
133
+ }
134
+
110
135
  /** Split a comma/whitespace-separated tool list into trimmed, non-empty names. */
111
136
  function parseToolList(raw: string | undefined): string[] {
112
137
  if (!raw) return [];
@@ -174,8 +199,11 @@ export function loadPiConfig(): ResolvedPiConfig {
174
199
  const binaryOverride =
175
200
  typeof cfg.binary === "string" && cfg.binary.length > 0 ? cfg.binary : undefined;
176
201
 
202
+ const mode = resolveMode(cfg.mode);
203
+
177
204
  return {
178
- mode: resolveMode(cfg.mode),
205
+ mode,
206
+ routeShell: resolveRouteShell(mode, cfg.routeShell),
179
207
  enableMcp,
180
208
  binaryOverride,
181
209
  forwardedEnv,
@@ -333,12 +333,25 @@ export default async function (pi: ExtensionAPI) {
333
333
  process.env.LEAN_CTX_COMPRESS = "1";
334
334
  process.env.LEAN_CTX_SAVINGS_FOOTER ??= "always";
335
335
 
336
- // Defer setActiveTools to session_start — runtime actions aren't available during extension load
337
- // In "replace" mode, disable Pi builtins and only expose ctx_* tools.
338
- // In "additive" mode (default), keep Pi builtins alongside ctx_* tools.
339
- if (PI_MODE === "replace") {
336
+ // Defer setActiveTools to session_start — runtime actions aren't available
337
+ // during extension load. Which Pi builtins to suppress:
338
+ // - "replace" mode → all five (read/bash/ls/find/grep): expose only ctx_*.
339
+ // - "additive" + routeShell → just `bash`: route shell through ctx_shell so
340
+ // build/test output is compressed and metered, while the read/list/search
341
+ // builtins stay available next to ctx_*. Without this, an agent offered
342
+ // both `bash` and `ctx_shell` picks the uncompressed native `bash` (the R1
343
+ // finding: 102 bash / 0 ctx_shell), so the heaviest addressable surface in
344
+ // a fix task — make/reproducer/test logs — never reaches the compressor.
345
+ // - "additive" without routeShell → suppress nothing (keep Pi builtins).
346
+ const suppressedBuiltins = PI_MODE === "replace"
347
+ ? DISABLED_BUILTIN_TOOLS
348
+ : PI_CONFIG.routeShell
349
+ ? new Set(["bash"])
350
+ : new Set<string>();
351
+
352
+ if (suppressedBuiltins.size > 0) {
340
353
  pi.on("session_start", () => {
341
- const activeTools = pi.getActiveTools().filter((name) => !DISABLED_BUILTIN_TOOLS.has(name));
354
+ const activeTools = pi.getActiveTools().filter((name) => !suppressedBuiltins.has(name));
342
355
  pi.setActiveTools(activeTools);
343
356
  });
344
357
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lean-ctx",
3
- "version": "3.8.5",
3
+ "version": "3.8.7",
4
4
  "description": "Pi Coding Agent extension — routes bash/read/grep/find/ls through lean-ctx for strong token savings. The embedded MCP bridge (on by default) adds a persistent session cache so unchanged re-reads cost ~13 tokens.",
5
5
  "keywords": [
6
6
  "pi-package",