pi-rtk-optimizer 0.8.0 → 0.8.2

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/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.2] - 2026-06-01
11
+
12
+ ### Changed
13
+ - Deferred output compactor and configuration modal loading during extension bootstrap.
14
+ - Replaced technique barrel imports with direct module imports.
15
+ - Migrated inline test entrypoints from Bun to Node/tsx.
16
+ - Widened Pi peer dependency ranges to include `^0.77.0 || ^0.78.0`.
17
+
18
+ ## [0.8.1] - 2026-05-26
19
+
20
+ ### Changed
21
+ - Widened `@earendil-works/pi-coding-agent` and `@earendil-works/pi-tui` peer dependency ranges to `^0.74.0 || ^0.75.0`.
22
+
10
23
  ## [0.8.0] - 2026-05-22
11
24
 
12
25
  ### Added
package/README.md CHANGED
@@ -257,7 +257,7 @@ Automatic fixes applied on Windows:
257
257
 
258
258
  - **Peer dependencies:** `@earendil-works/pi-coding-agent`, `@earendil-works/pi-tui`
259
259
  - **Runtime:** Node.js ≥20, optional `rtk` binary for command rewriting
260
- - **Development verification:** Node.js ≥20, npm, and Bun for the test scripts
260
+ - **Development verification:** Node.js ≥24 and npm for Node/tsx test scripts using Node's experimental test module mocks
261
261
 
262
262
  ## Development
263
263
 
@@ -268,7 +268,7 @@ npm run build
268
268
  # Full typecheck
269
269
  npm run typecheck
270
270
 
271
- # Run Bun-based tests
271
+ # Run Node/tsx tests
272
272
  npm run test
273
273
 
274
274
  # Full verification
package/package.json CHANGED
@@ -1,67 +1,67 @@
1
- {
2
- "name": "pi-rtk-optimizer",
3
- "version": "0.8.0",
4
- "description": "Pi extension that optimizes RTK command rewriting and tool output compaction for the coding agent.",
5
- "type": "module",
6
- "main": "./index.ts",
7
- "exports": {
8
- ".": "./index.ts"
9
- },
10
- "files": [
11
- "index.ts",
12
- "src",
13
- "config/config.example.json",
14
- "README.md",
15
- "CHANGELOG.md",
16
- "LICENSE"
17
- ],
18
- "scripts": {
19
- "build": "tsc -p tsconfig.json --noCheck",
20
- "typecheck": "tsc -p tsconfig.json",
21
- "test": "bun ./src/output-compactor-test.ts && bun ./src/command-rewriter-test.ts && bun ./src/runtime-guard-test.ts && bun ./src/additional-coverage-test.ts && bun ./src/config-modal-test.ts && bun ./src/index-test.ts",
22
- "check": "npm run typecheck && npm run test && npm run build:check",
23
- "build:check": "esbuild ./index.ts --bundle --platform=node --format=esm --outfile=./.pi-rtk-optimizer-check.mjs --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-tui && node -e \"import { unlinkSync } from 'node:fs'; unlinkSync('./.pi-rtk-optimizer-check.mjs');\""
24
- },
25
- "keywords": [
26
- "pi-package",
27
- "pi",
28
- "pi-extension",
29
- "pi-coding-agent",
30
- "coding-agent",
31
- "rtk",
32
- "token-optimization",
33
- "tool-compaction",
34
- "output-compaction",
35
- "command-rewrite",
36
- "optimization"
37
- ],
38
- "author": "MasuRii",
39
- "license": "MIT",
40
- "repository": {
41
- "type": "git",
42
- "url": "git+https://github.com/MasuRii/pi-rtk-optimizer.git"
43
- },
44
- "bugs": {
45
- "url": "https://github.com/MasuRii/pi-rtk-optimizer/issues"
46
- },
47
- "homepage": "https://github.com/MasuRii/pi-rtk-optimizer#readme",
48
- "engines": {
49
- "node": ">=20"
50
- },
51
- "publishConfig": {
52
- "access": "public"
53
- },
54
- "devDependencies": {
55
- "esbuild": "0.28.0",
56
- "typescript": "6.0.3"
57
- },
58
- "pi": {
59
- "extensions": [
60
- "./index.ts"
61
- ]
62
- },
63
- "peerDependencies": {
64
- "@earendil-works/pi-coding-agent": "^0.75.4",
65
- "@earendil-works/pi-tui": "^0.75.4"
66
- }
67
- }
1
+ {
2
+ "name": "pi-rtk-optimizer",
3
+ "version": "0.8.2",
4
+ "description": "Pi extension that optimizes RTK command rewriting and tool output compaction for the coding agent.",
5
+ "type": "module",
6
+ "main": "./index.ts",
7
+ "exports": {
8
+ ".": "./index.ts"
9
+ },
10
+ "files": [
11
+ "index.ts",
12
+ "src",
13
+ "config/config.example.json",
14
+ "README.md",
15
+ "CHANGELOG.md",
16
+ "LICENSE"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json --noCheck",
20
+ "typecheck": "tsc -p tsconfig.json",
21
+ "test": "bun ./src/output-compactor-test.ts && bun ./src/command-rewriter-test.ts && bun ./src/runtime-guard-test.ts && bun ./src/additional-coverage-test.ts && bun ./src/config-modal-test.ts && bun ./src/index-test.ts",
22
+ "check": "npm run typecheck && npm run test && npm run build:check",
23
+ "build:check": "esbuild ./index.ts --bundle --platform=node --format=esm --outfile=./.pi-rtk-optimizer-check.mjs --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-tui && node -e \"import { unlinkSync } from 'node:fs'; unlinkSync('./.pi-rtk-optimizer-check.mjs');\""
24
+ },
25
+ "keywords": [
26
+ "pi-package",
27
+ "pi",
28
+ "pi-extension",
29
+ "pi-coding-agent",
30
+ "coding-agent",
31
+ "rtk",
32
+ "token-optimization",
33
+ "tool-compaction",
34
+ "output-compaction",
35
+ "command-rewrite",
36
+ "optimization"
37
+ ],
38
+ "author": "MasuRii",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/MasuRii/pi-rtk-optimizer.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/MasuRii/pi-rtk-optimizer/issues"
46
+ },
47
+ "homepage": "https://github.com/MasuRii/pi-rtk-optimizer#readme",
48
+ "engines": {
49
+ "node": ">=20"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "devDependencies": {
55
+ "esbuild": "0.28.0",
56
+ "typescript": "6.0.3"
57
+ },
58
+ "pi": {
59
+ "extensions": [
60
+ "./index.ts"
61
+ ]
62
+ },
63
+ "peerDependencies": {
64
+ "@earendil-works/pi-coding-agent": "^0.74.0 || ^0.75.0",
65
+ "@earendil-works/pi-tui": "^0.74.0 || ^0.75.0"
66
+ }
67
+ }
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
3
- import { mock } from "bun:test";
3
+ import { mock } from "node:test";
4
4
 
5
5
  import { clearOutputMetrics, getOutputMetricsSummary, trackOutputSavings } from "./output-metrics.ts";
6
6
  import { runTest } from "./test-helpers.ts";
@@ -13,9 +13,11 @@ import { sanitizeStreamingBashExecutionResult } from "./tool-execution-sanitizer
13
13
  import { sanitizeRtkEmojiOutput } from "./techniques/emoji.ts";
14
14
  import { stripRtkHookWarnings } from "./techniques/rtk.ts";
15
15
 
16
- mock.module("@earendil-works/pi-coding-agent", () => ({
17
- getAgentDir: () => "/tmp/.pi/agent",
18
- }));
16
+ mock.module("@earendil-works/pi-coding-agent", {
17
+ namedExports: {
18
+ getAgentDir: () => "/tmp/.pi/agent",
19
+ },
20
+ });
19
21
 
20
22
  const {
21
23
  ensureConfigExists,
@@ -0,0 +1,31 @@
1
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
+ import { getRtkArgumentCompletions } from "./command-completions.js";
3
+ import type { RtkIntegrationConfig, RuntimeStatus } from "./types.js";
4
+
5
+ export interface RtkIntegrationController {
6
+ getConfig(): RtkIntegrationConfig;
7
+ setConfig(next: RtkIntegrationConfig, ctx: ExtensionCommandContext): void;
8
+ getConfigPath(): string;
9
+ getRuntimeStatus(): RuntimeStatus;
10
+ refreshRuntimeStatus(): Promise<RuntimeStatus>;
11
+ getMetricsSummary(): string;
12
+ clearMetrics(): void;
13
+ }
14
+
15
+ let commandModalModulePromise: Promise<typeof import("./config-modal.js")> | undefined;
16
+
17
+ function loadCommandModalModule(): Promise<typeof import("./config-modal.js")> {
18
+ commandModalModulePromise ??= import("./config-modal.js");
19
+ return commandModalModulePromise;
20
+ }
21
+
22
+ export function registerRtkIntegrationCommand(pi: ExtensionAPI, controller: RtkIntegrationController): void {
23
+ pi.registerCommand("rtk", {
24
+ description: "Configure RTK rewrite and output compaction integration",
25
+ getArgumentCompletions: getRtkArgumentCompletions,
26
+ handler: async (args, ctx) => {
27
+ const { handleRtkIntegrationCommand } = await loadCommandModalModule();
28
+ await handleRtkIntegrationCommand(args, ctx, controller);
29
+ },
30
+ });
31
+ }
@@ -1,40 +1,44 @@
1
1
  import assert from "node:assert/strict";
2
- import { mock } from "bun:test";
2
+ import { mock } from "node:test";
3
3
 
4
4
  import { cloneDefaultConfig, runTest } from "./test-helpers.ts";
5
5
 
6
- mock.module("@earendil-works/pi-coding-agent", () => ({
7
- getAgentDir: () => "/tmp/.pi/agent",
8
- getSettingsListTheme: () => ({}),
9
- }));
6
+ mock.module("@earendil-works/pi-coding-agent", {
7
+ namedExports: {
8
+ getAgentDir: () => "/tmp/.pi/agent",
9
+ getSettingsListTheme: () => ({}),
10
+ },
11
+ });
10
12
 
11
13
  const settingsListInputs: string[] = [];
12
14
  const settingsListUpdates: Array<{ id: string; value: string }> = [];
13
15
 
14
- mock.module("@earendil-works/pi-tui", () => ({
15
- Box: class {
16
- addChild(): void {}
17
- },
18
- Container: class {
19
- addChild(): void {}
20
- render(): string[] {
21
- return ["settings-content"];
22
- }
23
- invalidate(): void {}
24
- },
25
- SettingsList: class {
26
- handleInput(data: string): void {
27
- settingsListInputs.push(data);
28
- }
29
- updateValue(id: string, value: string): void {
30
- settingsListUpdates.push({ id, value });
31
- }
16
+ mock.module("@earendil-works/pi-tui", {
17
+ namedExports: {
18
+ Box: class {
19
+ addChild(): void {}
20
+ },
21
+ Container: class {
22
+ addChild(): void {}
23
+ render(): string[] {
24
+ return ["settings-content"];
25
+ }
26
+ invalidate(): void {}
27
+ },
28
+ SettingsList: class {
29
+ handleInput(data: string): void {
30
+ settingsListInputs.push(data);
31
+ }
32
+ updateValue(id: string, value: string): void {
33
+ settingsListUpdates.push({ id, value });
34
+ }
35
+ },
36
+ Spacer: class {},
37
+ Text: class {},
38
+ truncateToWidth: (text: string, width: number) => text.slice(0, width),
39
+ visibleWidth: (text: string) => text.length,
32
40
  },
33
- Spacer: class {},
34
- Text: class {},
35
- truncateToWidth: (text: string, width: number) => text.slice(0, width),
36
- visibleWidth: (text: string) => text.length,
37
- }));
41
+ });
38
42
 
39
43
  function stripAnsi(text: string): string {
40
44
  return text.replace(/\x1b\[[0-9;]*m/g, "");
@@ -10,7 +10,7 @@ import {
10
10
  type RuntimeStatus,
11
11
  } from "./types.js";
12
12
 
13
- interface RtkIntegrationController {
13
+ export interface RtkIntegrationController {
14
14
  getConfig(): RtkIntegrationConfig;
15
15
  setConfig(next: RtkIntegrationConfig, ctx: ExtensionCommandContext): void;
16
16
  getConfigPath(): string;
@@ -584,21 +584,27 @@ async function handleArgs(
584
584
  return true;
585
585
  }
586
586
 
587
+ export async function handleRtkIntegrationCommand(
588
+ args: string,
589
+ ctx: ExtensionCommandContext,
590
+ controller: RtkIntegrationController,
591
+ ): Promise<void> {
592
+ if (await handleArgs(args, ctx, controller)) {
593
+ return;
594
+ }
595
+
596
+ if (!ctx.hasUI) {
597
+ ctx.ui.notify("/rtk requires interactive TUI mode.", "warning");
598
+ return;
599
+ }
600
+
601
+ await openSettingsModal(ctx, controller);
602
+ }
603
+
587
604
  export function registerRtkIntegrationCommand(pi: ExtensionAPI, controller: RtkIntegrationController): void {
588
605
  pi.registerCommand("rtk", {
589
606
  description: "Configure RTK rewrite and output compaction integration",
590
607
  getArgumentCompletions: getRtkArgumentCompletions,
591
- handler: async (args, ctx) => {
592
- if (await handleArgs(args, ctx, controller)) {
593
- return;
594
- }
595
-
596
- if (!ctx.hasUI) {
597
- ctx.ui.notify("/rtk requires interactive TUI mode.", "warning");
598
- return;
599
- }
600
-
601
- await openSettingsModal(ctx, controller);
602
- },
608
+ handler: (args, ctx) => handleRtkIntegrationCommand(args, ctx, controller),
603
609
  });
604
610
  }
package/src/index-test.ts CHANGED
@@ -1,32 +1,36 @@
1
1
  import assert from "node:assert/strict";
2
- import { mock } from "bun:test";
2
+ import { mock } from "node:test";
3
3
 
4
4
  import { runTest } from "./test-helpers.ts";
5
5
 
6
- mock.module("@earendil-works/pi-coding-agent", () => ({
7
- getAgentDir: () => "/tmp/.pi/agent",
8
- getSettingsListTheme: () => ({}),
9
- isToolCallEventType: (toolName: string, event: Record<string, unknown>) => event.toolName === toolName,
10
- }));
11
-
12
- mock.module("@earendil-works/pi-tui", () => ({
13
- Box: class {},
14
- Container: class {
15
- addChild(): void {}
16
- render(): string[] {
17
- return [];
18
- }
19
- invalidate(): void {}
6
+ mock.module("@earendil-works/pi-coding-agent", {
7
+ namedExports: {
8
+ getAgentDir: () => "/tmp/.pi/agent",
9
+ getSettingsListTheme: () => ({}),
10
+ isToolCallEventType: (toolName: string, event: Record<string, unknown>) => event.toolName === toolName,
20
11
  },
21
- SettingsList: class {
22
- handleInput(): void {}
23
- updateValue(): void {}
12
+ });
13
+
14
+ mock.module("@earendil-works/pi-tui", {
15
+ namedExports: {
16
+ Box: class {},
17
+ Container: class {
18
+ addChild(): void {}
19
+ render(): string[] {
20
+ return [];
21
+ }
22
+ invalidate(): void {}
23
+ },
24
+ SettingsList: class {
25
+ handleInput(): void {}
26
+ updateValue(): void {}
27
+ },
28
+ Spacer: class {},
29
+ Text: class {},
30
+ truncateToWidth: (text: string) => text,
31
+ visibleWidth: (text: string) => text.length,
24
32
  },
25
- Spacer: class {},
26
- Text: class {},
27
- truncateToWidth: (text: string) => text,
28
- visibleWidth: (text: string) => text.length,
29
- }));
33
+ });
30
34
 
31
35
  const indexModule = await import("./index.ts");
32
36
  const { createBoundedNoticeTracker, shouldInjectSourceFilterTroubleshootingNote } = indexModule;
package/src/index.ts CHANGED
@@ -7,10 +7,10 @@ import {
7
7
  saveRtkIntegrationConfig,
8
8
  } from "./config-store.js";
9
9
  import { computeRewriteDecision } from "./command-rewriter.js";
10
- import { registerRtkIntegrationCommand } from "./config-modal.js";
10
+ import { registerRtkIntegrationCommand } from "./command-register.js";
11
11
  import { EXTENSION_NAME } from "./constants.js";
12
12
  import { clearOutputMetrics, getOutputMetricsSummary } from "./output-metrics.js";
13
- import { compactToolResult, type ToolResultCompactionMetadata } from "./output-compactor.js";
13
+ import type { ToolResultCompactionMetadata } from "./output-compactor.js";
14
14
  import { toRecord } from "./record-utils.js";
15
15
  import { applyRtkCommandEnvironment } from "./rtk-command-environment.js";
16
16
  import { resolveRtkExecutable, type RtkExecutableResolution } from "./rtk-executable-resolver.js";
@@ -31,6 +31,13 @@ function trimMessage(raw: string, maxLength = 220): string {
31
31
  const SOURCE_FILTER_TROUBLESHOOTING_NOTE =
32
32
  "RTK note: If file edits repeatedly fail because old text does not match, ask the user to manually run '/rtk' in the Pi TUI, disable 'Read compaction enabled', re-read the file, apply the edit, then ask the user to manually re-enable it in the Pi TUI.";
33
33
 
34
+ let outputCompactorModulePromise: Promise<typeof import("./output-compactor.js")> | undefined;
35
+
36
+ function loadOutputCompactorModule(): Promise<typeof import("./output-compactor.js")> {
37
+ outputCompactorModulePromise ??= import("./output-compactor.js");
38
+ return outputCompactorModulePromise;
39
+ }
40
+
34
41
  export function shouldInjectSourceFilterTroubleshootingNote(config: RtkIntegrationConfig): boolean {
35
42
  const compaction = config.outputCompaction;
36
43
  return (
@@ -436,6 +443,7 @@ export default function rtkIntegrationExtension(pi: ExtensionAPI): void {
436
443
  }
437
444
 
438
445
  try {
446
+ const { compactToolResult } = await loadOutputCompactorModule();
439
447
  const outcome = compactToolResult(
440
448
  {
441
449
  toolName: event.toolName,
@@ -1,14 +1,16 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { join } from "node:path";
3
- import { mock } from "bun:test";
3
+ import { mock } from "node:test";
4
4
 
5
5
  import { cloneDefaultConfig, runTest } from "./test-helpers.ts";
6
6
 
7
7
  const TEST_AGENT_DIR = "/tmp/.pi/agent";
8
8
 
9
- mock.module("@earendil-works/pi-coding-agent", () => ({
10
- getAgentDir: () => TEST_AGENT_DIR,
11
- }));
9
+ mock.module("@earendil-works/pi-coding-agent", {
10
+ namedExports: {
11
+ getAgentDir: () => TEST_AGENT_DIR,
12
+ },
13
+ });
12
14
 
13
15
  const { compactToolResult } = await import("./output-compactor.ts");
14
16
 
@@ -1,5 +1,7 @@
1
1
  import { toRecord } from "./record-utils.js";
2
- import { sanitizeRtkEmojiOutput, stripAnsiFast, stripRtkHookWarnings } from "./techniques/index.js";
2
+ import { stripAnsiFast } from "./techniques/ansi.js";
3
+ import { sanitizeRtkEmojiOutput } from "./techniques/emoji.js";
4
+ import { stripRtkHookWarnings } from "./techniques/rtk.js";
3
5
 
4
6
  interface ToolResultTextBlock {
5
7
  type: string;
@@ -162,9 +162,9 @@ declare module "node:assert/strict" {
162
162
  export default assert;
163
163
  }
164
164
 
165
- declare module "bun:test" {
165
+ declare module "node:test" {
166
166
  export const mock: {
167
- module(specifier: string, factory: () => Record<string, unknown>): void;
167
+ module(specifier: string, options: { namedExports?: Record<string, unknown>; defaultExport?: unknown }): void;
168
168
  };
169
169
  }
170
170