@aaroncql/pim-agent 0.0.1 → 0.2.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 (84) hide show
  1. package/README.md +94 -66
  2. package/bin/pim.ts +55 -3
  3. package/package.json +20 -5
  4. package/src/extensions/_init/index.ts +3 -2
  5. package/src/extensions/apply-patch/coordinator.ts +49 -0
  6. package/src/extensions/apply-patch/executor.ts +566 -0
  7. package/src/extensions/apply-patch/index.ts +74 -0
  8. package/src/extensions/apply-patch/matcher.ts +66 -0
  9. package/src/extensions/apply-patch/model.ts +34 -0
  10. package/src/extensions/apply-patch/parser.ts +381 -0
  11. package/src/extensions/apply-patch/render.ts +261 -0
  12. package/src/extensions/apply-patch/schema.ts +43 -0
  13. package/src/extensions/apply-patch/types.ts +30 -0
  14. package/src/extensions/bash/index.ts +3 -3
  15. package/src/extensions/edit/index.ts +2 -1
  16. package/src/extensions/glob/index.ts +3 -1
  17. package/src/extensions/glob/schema.ts +2 -1
  18. package/src/extensions/grep/index.ts +3 -1
  19. package/src/extensions/grep/render.ts +18 -4
  20. package/src/extensions/grep/schema.ts +1 -1
  21. package/src/extensions/read/index.ts +36 -9
  22. package/src/extensions/read/render.ts +31 -3
  23. package/src/extensions/subagent/index.ts +4 -1
  24. package/src/extensions/todo/index.ts +4 -3
  25. package/src/extensions/web-search/index.ts +2 -1
  26. package/src/extensions/write/index.ts +2 -1
  27. package/src/shared/PatchSummary.ts +82 -0
  28. package/src/telegram/Renderer.ts +190 -4
  29. package/src/extensions/bash/capture.test.ts +0 -126
  30. package/src/extensions/bash/format.test.ts +0 -240
  31. package/src/extensions/bash/run.test.ts +0 -262
  32. package/src/extensions/command-picker/ranker.test.ts +0 -46
  33. package/src/extensions/edit/edit.test.ts +0 -285
  34. package/src/extensions/file-picker/catalog.test.ts +0 -263
  35. package/src/extensions/file-picker/index.test.ts +0 -168
  36. package/src/extensions/file-picker/ranker.test.ts +0 -94
  37. package/src/extensions/footer/git.test.ts +0 -76
  38. package/src/extensions/footer/index.test.ts +0 -161
  39. package/src/extensions/footer/segments.test.ts +0 -164
  40. package/src/extensions/glob/glob.test.ts +0 -171
  41. package/src/extensions/glob/index.test.ts +0 -68
  42. package/src/extensions/glob/render.test.ts +0 -126
  43. package/src/extensions/grep/grep.test.ts +0 -387
  44. package/src/extensions/grep/index.test.ts +0 -68
  45. package/src/extensions/grep/render.test.ts +0 -269
  46. package/src/extensions/read/read.test.ts +0 -177
  47. package/src/extensions/read/render.test.ts +0 -61
  48. package/src/extensions/subagent/index.test.ts +0 -44
  49. package/src/extensions/subagent/render.test.ts +0 -292
  50. package/src/extensions/subagent/subagent.test.ts +0 -315
  51. package/src/extensions/system-prompt/prompt.test.ts +0 -64
  52. package/src/extensions/todo/index.test.ts +0 -244
  53. package/src/extensions/todo/render.test.ts +0 -180
  54. package/src/extensions/todo/todo.test.ts +0 -222
  55. package/src/extensions/tps/index.test.ts +0 -254
  56. package/src/extensions/web-fetch/WebViewMarkdownSnapshot.test.ts +0 -119
  57. package/src/extensions/web-fetch/fetch.test.ts +0 -244
  58. package/src/extensions/web-fetch/render.test.ts +0 -56
  59. package/src/extensions/web-search/ExaMcpClient.test.ts +0 -143
  60. package/src/extensions/web-search/render.test.ts +0 -21
  61. package/src/extensions/web-search/search.test.ts +0 -53
  62. package/src/extensions/working-indicator/index.test.ts +0 -21
  63. package/src/extensions/write/render.test.ts +0 -64
  64. package/src/extensions/write/write.test.ts +0 -108
  65. package/src/shared/DiffLines.test.ts +0 -193
  66. package/src/shared/DiffRenderer.test.ts +0 -206
  67. package/src/shared/EditMatcher.test.ts +0 -123
  68. package/src/shared/FileScanner.test.ts +0 -158
  69. package/src/shared/FuzzyMatcher.test.ts +0 -114
  70. package/src/shared/GitignoreFilter.test.ts +0 -64
  71. package/src/shared/Lines.test.ts +0 -25
  72. package/src/shared/McpClient.test.ts +0 -235
  73. package/src/shared/OutputBudget.test.ts +0 -99
  74. package/src/shared/Paths.test.ts +0 -51
  75. package/src/shared/PimSettings.test.ts +0 -90
  76. package/src/shared/Renderer.test.ts +0 -190
  77. package/src/shared/SpillCache.test.ts +0 -94
  78. package/src/shared/Tools.test.ts +0 -392
  79. package/src/telegram/Config.test.ts +0 -275
  80. package/src/telegram/Markdown.test.ts +0 -143
  81. package/src/telegram/Renderer.test.ts +0 -216
  82. package/src/telegram/SessionRegistry.test.ts +0 -89
  83. package/src/telegram/TaskScheduler.test.ts +0 -278
  84. package/src/telegram/TaskTool.test.ts +0 -179
package/README.md CHANGED
@@ -3,26 +3,29 @@
3
3
 
4
4
  _**Pim is to Pi what Vim is to Vi.**_
5
5
 
6
- An opinionated yet minimal, Bun-native extension pack for [Pi](https://pi.dev/): revamped tools, ANSI-compatible themes, fzf-style autocompletions, Telegram bot, and more. Scores up to [41.6% on Terminal-Bench 2.0](#terminal-bench-20) with locally hosted Qwen3.6-35B, matching Claude Code + Sonnet 4.5.
6
+ A Bun-native extension pack for [Pi](https://pi.dev/): web access, subagents, revamped core tools, ANSI-compatible themes, fzf-style completions, Telegram mode, and more. Preliminary score of [37.8% on Terminal-Bench 2.0](#terminal-bench-20) with locally hosted Qwen3.6-35B, rivalling Claude Code + Sonnet 4.5.
7
7
 
8
8
  - [Quick Start](#quick-start)
9
+ - [Enabling/Disabling Extensions](#enablingdisabling-extensions)
9
10
  - [API Keys (Optional)](#api-keys-optional)
10
- - [Recommended Settings (Optional)](#recommended-settings-optional)
11
+ - [Recommended Pi Settings (Optional)](#recommended-pi-settings-optional)
12
+ - [Why Pim?](#why-pim)
13
+ - [Lean System Prompt](#lean-system-prompt)
14
+ - [Model-Aware Tools](#model-aware-tools)
15
+ - [Terminal-Bench 2.0](#terminal-bench-20)
11
16
  - [Agent Tools](#agent-tools)
12
17
  - [Terminal UI](#terminal-ui)
13
18
  - [Telegram Bot](#telegram-bot)
14
19
  - [Setup](#setup)
15
20
  - [Commands](#commands)
16
21
  - [Features](#features)
17
- - [Why Pim?](#why-pim)
18
- - [Harness Design](#harness-design)
19
- - [Terminal-Bench 2.0](#terminal-bench-20)
20
22
  - [Developing](#developing)
21
23
 
24
+ ![Pim Demo](https://raw.githubusercontent.com/AaronCQL/pim-agent/refs/heads/main/assets/demo.webp)
25
+
22
26
  ## Quick Start
23
27
 
24
- > [!IMPORTANT]
25
- > The following instructions assume you have [Pi](https://pi.dev/docs/latest/quickstart) and [Bun](https://bun.com/docs/installation) already installed. If not, install them first (_or ask your agent to do it for you_). For all things related to Pi, refer to [Pi's comprehensive docs](https://pi.dev/docs/latest).
28
+ Ensure that you have [Pi](https://pi.dev/docs/latest/quickstart) and [Bun](https://bun.com/docs/installation) already installed. If not, install them first (_or ask your agent to do it for you_). For all things related to Pi, refer to [Pi's comprehensive docs](https://pi.dev/docs/latest).
26
29
 
27
30
  ```sh
28
31
  # First, install Pim as a Pi extension:
@@ -35,11 +38,24 @@ bun install -g @aaroncql/pim-agent
35
38
  pim
36
39
  ```
37
40
 
38
- The `pim` command is a [thin Bun launcher](./bin/pim.ts) that wraps around `pi` so that Bun-specific tooling and APIs can be used. Other Pi extensions should continue to work normally.
41
+ > [!IMPORTANT]
42
+ > **Use `pim` instead of `pi` after installing Pim.** The `pim` command is a drop-in replacement for `pi` that [runs Pi via Bun](./bin/pim.ts), enabling Bun-specific APIs. Existing Pi behaviour and extensions should continue to work normally.
43
+
44
+ If `pim` cannot locate Pi, make sure `pi` is on your `PATH`, or set:
45
+
46
+ ```sh
47
+ PIM_PI_CLI=/path/to/pi/dist/cli.js pim
48
+ ```
49
+
50
+ ### Enabling/Disabling Extensions
51
+
52
+ Pim ships a collection of extensions which are all enabled by default. To disable specific ones that don't suit your needs, run `pim config` and toggle them there.
53
+
54
+ Some Pim extensions can be toggled directly within the TUI as well: `/powerline` for the Git-aware powerline footer, `/tps` for inference speed reporting.
39
55
 
40
56
  ### API Keys (Optional)
41
57
 
42
- Pim's web tools use [Exa](https://exa.ai) for searching the web and [Jina](https://jina.ai/reader/) for fetching websites as Markdown. Without API keys, the tools are subject to the following rate limits (as of May 2026):
58
+ Pim's web tools use [Exa](https://exa.ai) for searching the web and [Jina](https://jina.ai/reader/) for fetching websites as Markdown. These tools still work without API keys, but are subject to the following rate limits (as of May 2026):
43
59
 
44
60
  - Exa - 1,000 requests per month
45
61
  - Jina - 20 requests per minute
@@ -63,7 +79,7 @@ Environment variables override `settings.json` when present:
63
79
  EXA_API_KEY='api_key_here' JINA_API_KEY='api_key_here' pim
64
80
  ```
65
81
 
66
- ### Recommended Settings (Optional)
82
+ ### Recommended Pi Settings (Optional)
67
83
 
68
84
  Add the following settings to your `~/.pi/agent/settings.json` for the best experience with Pim:
69
85
 
@@ -77,10 +93,69 @@ Add the following settings to your `~/.pi/agent/settings.json` for the best expe
77
93
  }
78
94
  ```
79
95
 
96
+ ## Why Pim?
97
+
98
+ Pim's philosophy is **opinionated but minimal**. Its goal is to improve the out-of-the-box experience for both users and agents, without sacrificing composability with other Pi extensions.
99
+
100
+ ### Lean System Prompt
101
+
102
+ Pim's system prompt is just **~3K tokens** despite exposing 10+ tools, far leaner than alternatives like OpenCode (~10K) or Hermes (~16K).
103
+
104
+ This is achieved by having tool descriptions focus on _how_ to use each tool instead of prescribing _when_, since models already appear to internally encode when tools are needed, and prompting them to call tools can [suppress both necessary and unnecessary calls](https://arxiv.org/abs/2605.09252).
105
+
106
+ ### Model-Aware Tools
107
+
108
+ LLMs are [increasingly post-trained](https://openai.com/index/introducing-codex) for specific agent harnesses, making tool schemas part of the model's learned interface. For text-file editing, Anthropic models are trained to use [string replacement operations](https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool), while OpenAI models use [V4A patch operations](https://developers.openai.com/api/docs/guides/tools-apply-patch).
109
+
110
+ Pim keeps the active toolset model-aware instead of assuming one tool fits every LLM. It dynamically exposes the tools best suited to the selected model, giving each model the interface that best matches its learned behaviour while keeping the prompt lean.
111
+
112
+ ### Terminal-Bench 2.0
113
+
114
+ | ID | Pim Version | LLM / Model | Results |
115
+ | --- | --- | --- | --- |
116
+ | [r1](./benchmarks/terminal_bench_2/results/r1/) | [`21d084d1`](https://github.com/AaronCQL/pim-agent/tree/21d084d1) | `Qwen3.6-35B-A3B-UD-Q6_K_XL.gguf` | **41.6%** (37/89) |
117
+ | [r2](./benchmarks/terminal_bench_2/results/r2/) | [`bfd792cf`](https://github.com/AaronCQL/pim-agent/tree/bfd792cf) | `Qwen3.6-35B-A3B-UD-Q6_K_XL.gguf` | **36.0%** (32/89) |
118
+ | [r3](./benchmarks/terminal_bench_2/results/r3/) | [`cd52f3a4`](https://github.com/AaronCQL/pim-agent/tree/cd52f3a4) | `Qwen3.6-35B-A3B-UD-Q6_K_XL.gguf` | **36.0%** (32/89) |
119
+
120
+ Preliminary aggregate score of **37.8%** from 3 independent runs. Each ran on an incremental build of Pim, though changes between runs were minor and none were tuned to the benchmark. Pim's `subagent` tool was disabled for all runs to keep each trial single-agent.
121
+
122
+ On average, Pim solves **~54% more tasks** than [little-coder](https://github.com/itayinbarr/little-coder) with the same Qwen3.6-35B model (37.8% vs 24.6%). This also places Pim in a similar tier to Claude Code + Sonnet 4.5 (40.1%), and above Codex + GPT-5-Mini (31.9%).
123
+
124
+ The Qwen3.6-35B model is hosted via llama.cpp on an M4 Pro 48GB MacBook, with the following config:
125
+
126
+ ```sh
127
+ llama-server \
128
+ -c 131072 \
129
+ -ngl 99 \
130
+ --slot-save-path /tmp/llama-slots \
131
+ --flash-attn on \
132
+ --cache-type-k q8_0 \
133
+ --cache-type-v q8_0 \
134
+ --jinja \
135
+ --temp 0.6 \
136
+ --top-p 0.95 \
137
+ --top-k 20 \
138
+ --min-p 0.0 \
139
+ --presence-penalty 0.0 \
140
+ --repeat-penalty 1.0 \
141
+ --reasoning-budget 16384 \
142
+ --reasoning-budget-message "Alright, I've thought enough. Let me take the next concrete step now — either a tool call or a final answer — and refine based on what I learn." \
143
+ -np 1
144
+ ```
145
+
146
+ _Note 1_: results are preliminary as only 3 independent full runs were conducted; Terminal-Bench 2.0 requires 5 independent full runs under a fixed configuration for an official score.
147
+
148
+ _Note 2_: the gap with little-coder may be partly explained by different inference configs (128K context vs 32K, Q6_K_XL vs Q4_K_M, higher thinking budget, etc.).
149
+
150
+ _Note 3_: in r1 and r3, the `code-from-image` trial was counted as non-passing because Qwen autonomously searched for the answer online after legitimately trying for a while.
151
+
152
+ _Note 4_: see the [`benchmarks/terminal_bench_2`](./benchmarks/terminal_bench_2/) dir for breakdown of results and reproduction steps.
153
+
80
154
  ## Agent Tools
81
155
 
82
- Pim revamps Pi's default tools (`bash`, `read`, `write`, `edit`) and adds the following:
156
+ Pim revamps Pi's default tools (`bash`, `read`, `write`, `edit`) so they produce consistent behaviour and output, cross-reference each other where useful, and render uniformly in the TUI. It also adds:
83
157
 
158
+ - **`apply_patch`** - V4A patch editing, dynamically exposed instead of `edit` for OpenAI models
84
159
  - **`glob`** - file enumeration by glob pattern, sorted newest-first, respects `.gitignore`
85
160
  - **`grep`** - regex search across files with context lines, multiline matching, respects `.gitignore`
86
161
  - **`web_search`** - search the web via [Exa](https://exa.ai) with ranked results and snippets
@@ -92,11 +167,11 @@ Pim revamps Pi's default tools (`bash`, `read`, `write`, `edit`) and adds the fo
92
167
 
93
168
  Pim also ships with quality of life improvements for the TUI:
94
169
 
95
- - **ANSI-compatible themes** - `pim-light` and `pim-dark` themes that match your terminal's colour scheme
170
+ - **ANSI-compatible themes** - `pim-light` and `pim-dark` themes which adapt to your terminal's colour scheme
96
171
  - **fzf-style autocomplete** - `@path` file picker and `/command` picker with fuzzy search
97
- - **Git-aware powerline footer** - current dir, git branch and states, context usage, model and session cost (toggle with `/powerline`)
172
+ - **Git-aware powerline footer** - cwd, git branch and states, context usage, model and session cost (toggle with `/powerline`)
98
173
  - **TPS reporting** - per-cycle decode/prefill rate, TTFT, and cache read tokens (toggle with `/tps`)
99
- - **Concise tool headers** - minimal one-liner title across all tool calls, `Ctrl+O` to toggle full details
174
+ - **Concise tool UI** - minimal one-liner title across all tool calls, `Ctrl+O` to toggle full details
100
175
 
101
176
  ## Telegram Bot
102
177
 
@@ -149,58 +224,11 @@ For development, run standalone with `pim --mode telegram` instead.
149
224
 
150
225
  ### Features
151
226
 
152
- - **Scheduled tasks** - your bot can create one-time, interval, or cron-based tasks that fire automatically; ask your bot to schedule something.
153
- - **Rich media** - send photos, documents, videos, audio, and voice messages directly in chat; your bot can also send files back to you.
154
- - **Thread-specific prompts** - each chat (or thread) gets its own session and instructions; ask your bot to modify its instructions.
155
-
156
- ## Why Pim?
157
-
158
- ### Harness Design
159
-
160
- Pim overrides Pi's default tools (`bash`, `read`, `write`, `edit`) so that all tools produce consistent, structured output for the model, cross-reference each other where useful, and render uniformly in the TUI.
161
-
162
- The system prompt is also kept as minimal as possible: at just ~3K tokens despite having 10+ tools (vs OpenCode's ~10K, Hermes' ~16K), with tool descriptions focusing on _how_ to use each tool instead of prescribing _when_. The rationale is that models already appear to internally encode when tools are needed, and prompting them to call tools can [suppress both necessary and unnecessary calls](https://arxiv.org/abs/2605.09252).
163
-
164
- ### Terminal-Bench 2.0
165
-
166
- Preliminary results from two full runs:
167
-
168
- | ID | Pim Version | LLM / Model | Results |
169
- | --- | --- | --- | --- |
170
- | [r1](./benchmarks/terminal_bench_2/results/r1/) | [`21d084d1`](https://github.com/AaronCQL/pim-agent/tree/21d084d1) | `Qwen3.6-35B-A3B-UD-Q6_K_XL.gguf` | **41.6%** (37/89) |
171
- | [r2](./benchmarks/terminal_bench_2/results/r2/) | [`bfd792cf`](https://github.com/AaronCQL/pim-agent/tree/bfd792cf) | `Qwen3.6-35B-A3B-UD-Q6_K_XL.gguf` | **36.0%** (32/89) |
172
-
173
- Comparing against the same Qwen3.6-35B model, Pim solves up to **70% more tasks** than [little-coder](https://github.com/itayinbarr/little-coder) (41.6% vs 24.6%). This puts Pim, with a locally hosted model, in a similar tier to Claude Code + Sonnet 4.5 (40.1%) and above Codex + GPT-5-Mini (31.9%).
174
-
175
- The Qwen3.6-35B model is hosted via llama.cpp on an M4 Pro 48GB MacBook, with the following config:
176
-
177
- ```sh
178
- llama-server \
179
- -c 131072 \
180
- -ngl 99 \
181
- --slot-save-path /tmp/llama-slots \
182
- --flash-attn on \
183
- --cache-type-k q8_0 \
184
- --cache-type-v q8_0 \
185
- --jinja \
186
- --temp 0.6 \
187
- --top-p 0.95 \
188
- --top-k 20 \
189
- --min-p 0.0 \
190
- --presence-penalty 0.0 \
191
- --repeat-penalty 1.0 \
192
- --reasoning-budget 16384 \
193
- --reasoning-budget-message "Alright, I've thought enough. Let me take the next concrete step now — either a tool call or a final answer — and refine based on what I learn." \
194
- -np 1
195
- ```
196
-
197
- _Note 1_: results are preliminary as only 2 independent full runs were conducted; Terminal-Bench 2.0 requires 5 independent full runs for an official score.
198
-
199
- _Note 2_: the gap with little-coder may be partly explained by different inference configs (128K context vs 32K, Q6_K_XL vs Q4_K_M, higher thinking budget, etc.).
200
-
201
- _Note 3_: in r1, the recorded result is actually 1 higher at 38/89. However, the `code-from-image` trial was excluded because Qwen autonomously searched for the answer online after 27 legitimate turns (see line 822 in [trajectory.json](benchmarks/terminal_bench_2/results/r1/code-from-image/trajectory.json)).
202
-
203
- _Note 4_: see the [`benchmarks/terminal_bench_2`](./benchmarks/terminal_bench_2/) dir for breakdown of results and reproduction steps.
227
+ - **Scheduled tasks** - your bot can create one-time, interval, or cron-based tasks that fire automatically; ask your bot to schedule something.
228
+ - 👀 **Live progress logs** - use `/logs` to choose what you see while the agent works: final replies, tool use, intermediate text, or thinking.
229
+ - 📝 **Markdown formatting** - replies render Markdown out of the box, including tables converted to vertical lists for Telegram.
230
+ - 📎 **Rich media** - send photos, documents, videos, audio, and voice messages directly in chat; your bot can also send files back to you.
231
+ - 🧵 **Thread-specific prompts** - each chat (or thread) gets its own session and optional instructions; ask your bot to modify its instructions.
204
232
 
205
233
  ## Developing
206
234
 
package/bin/pim.ts CHANGED
@@ -1,9 +1,20 @@
1
1
  #!/usr/bin/env bun
2
+ import { realpath } from "node:fs/promises";
2
3
  import { dirname, join } from "node:path";
3
4
 
4
5
  const PI_PACKAGE = "@earendil-works/pi-coding-agent";
5
6
 
6
- function findPiCli(): string {
7
+ async function findPiCli(): Promise<string> {
8
+ const envCli = await resolveEnvPiCli();
9
+ if (envCli) {
10
+ return envCli;
11
+ }
12
+
13
+ const pathCli = await resolvePathPiCli();
14
+ if (pathCli) {
15
+ return pathCli;
16
+ }
17
+
7
18
  const globalCli = resolveGlobalPiCli();
8
19
  if (globalCli) {
9
20
  return globalCli;
@@ -15,11 +26,52 @@ function findPiCli(): string {
15
26
  } catch {
16
27
  throw new Error(
17
28
  `Pim could not locate ${PI_PACKAGE}.\n` +
18
- `Install it globally under Bun: bun install -g ${PI_PACKAGE}`
29
+ `Install Pi from https://pi.dev/docs/latest/quickstart, or set PIM_PI_CLI=/path/to/cli.js`
19
30
  );
20
31
  }
21
32
  }
22
33
 
34
+ async function resolveEnvPiCli(): Promise<string | null> {
35
+ const candidate = process.env["PIM_PI_CLI"]?.trim();
36
+ if (!candidate) {
37
+ return null;
38
+ }
39
+ return (await isFile(candidate)) ? candidate : null;
40
+ }
41
+
42
+ async function resolvePathPiCli(): Promise<string | null> {
43
+ const piBin = Bun.which("pi");
44
+ if (!piBin) {
45
+ return null;
46
+ }
47
+
48
+ const cliPath = await resolveRealPath(piBin);
49
+ const pkgPath = join(dirname(cliPath), "..", "package.json");
50
+
51
+ try {
52
+ const pkg = (await Bun.file(pkgPath).json()) as { readonly name?: string };
53
+ return pkg.name === PI_PACKAGE ? cliPath : null;
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ async function resolveRealPath(path: string): Promise<string> {
60
+ try {
61
+ return await realpath(path);
62
+ } catch {
63
+ return path;
64
+ }
65
+ }
66
+
67
+ async function isFile(path: string): Promise<boolean> {
68
+ try {
69
+ return (await Bun.file(path).stat()).isFile();
70
+ } catch {
71
+ return false;
72
+ }
73
+ }
74
+
23
75
  function resolveGlobalPiCli(): string | null {
24
76
  const result = Bun.spawnSync({ cmd: ["bun", "pm", "-g", "bin"] });
25
77
  if (result.exitCode !== 0) {
@@ -78,7 +130,7 @@ if (mode === "telegram") {
78
130
  process.exit(0);
79
131
  }
80
132
 
81
- const piCli = findPiCli();
133
+ const piCli = await findPiCli();
82
134
  const proc = Bun.spawn({
83
135
  cmd: [process.execPath, piCli, ...cliArgs],
84
136
  stdio: [
package/package.json CHANGED
@@ -1,14 +1,28 @@
1
1
  {
2
2
  "name": "@aaroncql/pim-agent",
3
- "version": "0.0.1",
4
- "description": "Pim is to Pi what Vim is to Vi.",
3
+ "version": "0.2.0",
4
+ "description": "A Bun-native extension pack for Pi: web access, subagents, revamped core tools, ANSI-compatible themes, fzf-style completions, Telegram mode, and more.",
5
+ "license": "MIT",
5
6
  "type": "module",
7
+ "keywords": [
8
+ "pi-package",
9
+ "agent"
10
+ ],
11
+ "homepage": "https://github.com/AaronCQL/pim-agent#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/AaronCQL/pim-agent/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/AaronCQL/pim-agent.git"
18
+ },
6
19
  "bin": {
7
20
  "pim": "bin/pim.ts"
8
21
  },
9
22
  "files": [
10
23
  "bin/",
11
- "src/"
24
+ "src/",
25
+ "!src/**/*.test.ts"
12
26
  ],
13
27
  "engines": {
14
28
  "bun": ">=1.2.7"
@@ -19,14 +33,15 @@
19
33
  ],
20
34
  "themes": [
21
35
  "./src/themes"
22
- ]
36
+ ],
37
+ "image": "https://raw.githubusercontent.com/AaronCQL/pim-agent/refs/heads/main/assets/demo.webp"
23
38
  },
24
39
  "scripts": {
25
40
  "dev": "bun link && pim",
26
41
  "typecheck": "tsgo --noEmit",
27
42
  "test": "bun test src --only-failures",
28
43
  "lint": "oxlint . --fix",
29
- "format": "prettier --write --list-different package.json tsconfig.json .oxlintrc.json .prettierrc.json \"src/**/*.ts\" \"bin/**/*.ts\" \"**/*.md\"",
44
+ "format": "prettier --write --list-different package.json tsconfig.json .oxlintrc.json .prettierrc.json \"src/**/*.ts\" \"bin/**/*.ts\"",
30
45
  "check": "bun run typecheck && bun run test && bun run lint && bun run format"
31
46
  },
32
47
  "peerDependencies": {
@@ -21,8 +21,9 @@ export default async function (pi: ExtensionAPI): Promise<void> {
21
21
  if (typeof Bun === "undefined") {
22
22
  throw new Error(
23
23
  "Pim requires the Bun runtime.\n" +
24
- "Install pi via bun: bun install -g @earendil-works/pi-coding-agent\n" +
25
- "Then run: pim"
24
+ "Install the Pim launcher: bun install -g @aaroncql/pim-agent\n" +
25
+ "Then run: pim\n" +
26
+ "If pim cannot locate Pi, ensure `pi` is on PATH or set PIM_PI_CLI=/path/to/cli.js"
26
27
  );
27
28
  }
28
29
 
@@ -0,0 +1,49 @@
1
+ const EDIT_TOOL = "edit";
2
+ const APPLY_PATCH_TOOL = "apply_patch";
3
+
4
+ /**
5
+ * Pure reconcile over the active-tool list. Single-slot swap:
6
+ * - If neither `edit` nor `apply_patch` is active, no-op (respect user opt-out).
7
+ * - Otherwise keep exactly one in the same position: `apply_patch` when the
8
+ * model is GPT/Codex-family, else `edit`. All other active tools are
9
+ * preserved in order.
10
+ *
11
+ * Returns the same array reference when nothing changes so callers can skip the
12
+ * prompt-rebuilding `setActiveTools` call.
13
+ */
14
+ export function computeActiveTools(
15
+ active: readonly string[],
16
+ isGpt: boolean
17
+ ): readonly string[] {
18
+ const hasEdit = active.includes(EDIT_TOOL);
19
+ const hasApplyPatch = active.includes(APPLY_PATCH_TOOL);
20
+
21
+ if (!hasEdit && !hasApplyPatch) {
22
+ return active;
23
+ }
24
+
25
+ const desired = isGpt ? APPLY_PATCH_TOOL : EDIT_TOOL;
26
+ const drop = isGpt ? EDIT_TOOL : APPLY_PATCH_TOOL;
27
+
28
+ if (active.includes(desired) && !active.includes(drop)) {
29
+ return active;
30
+ }
31
+
32
+ const result: string[] = [];
33
+ let placed = false;
34
+ for (const tool of active) {
35
+ if (tool === EDIT_TOOL || tool === APPLY_PATCH_TOOL) {
36
+ if (!placed) {
37
+ result.push(desired);
38
+ placed = true;
39
+ }
40
+ continue;
41
+ }
42
+ result.push(tool);
43
+ }
44
+ if (!placed) {
45
+ result.push(desired);
46
+ }
47
+
48
+ return result;
49
+ }