@howaboua/pi-codex-conversion 1.0.31 → 1.5.0-dev.10.fab478e
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 +38 -118
- package/bin/apply_patch +38 -0
- package/bin/apply_patch.cmd +2 -0
- package/package.json +15 -10
- package/src/index.ts +10 -3
- package/src/patch/paths.ts +4 -9
- package/src/prompt/build-system-prompt.ts +7 -11
- package/src/tools/apply-patch-binary.ts +21 -0
- package/src/tools/apply-patch-tool.ts +1 -5
- package/src/tools/exec-command-tool.ts +7 -13
- package/src/tools/image-generation-tool.ts +1 -1
- package/src/tools/view-image-tool.ts +3 -5
- package/src/tools/write-stdin-tool.ts +5 -9
- package/vendor/apply-patch/darwin-arm64/apply_patch +0 -0
- package/vendor/apply-patch/darwin-x64/apply_patch +0 -0
- package/vendor/apply-patch/linux-arm64/apply_patch +0 -0
- package/vendor/apply-patch/linux-x64/apply_patch +0 -0
- package/vendor/apply-patch/win32-arm64/apply_patch.exe +0 -0
- package/vendor/apply-patch/win32-x64/apply_patch.exe +0 -0
package/README.md
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
# pi-codex-conversion
|
|
2
2
|
|
|
3
|
-
Codex-
|
|
3
|
+
Codex-style tools for [Pi](https://github.com/badlogic/pi-mono).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> [!NOTE]
|
|
6
|
+
> Use the npm package for normal installs. Avoid `pi install git:...` unless you know you want the development checkout; see [Development checkout](#development-checkout).
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
- saves native Codex image-generation outputs into `.pi/openai-codex-images/` at the workspace/repo root and mirrors the newest image to `.pi/openai-codex-images/latest.png`
|
|
9
|
-
- preserves Pi's composed system prompt and applies a narrow Codex-oriented delta on top
|
|
10
|
-
- renders exec activity with Codex-style command and background-terminal labels
|
|
11
|
-
- renders `apply_patch` calls with Codex-style `Added` / `Edited` / `Deleted` diff blocks and Pi-style colored diff lines
|
|
12
|
-
- targets modern Pi tool/rendering APIs and is aligned with Pi `0.74.x` / the `@earendil-works/*` package scope
|
|
8
|
+
GPT/Codex models are strongest when the tool surface looks like the Codex CLI they were trained around: shell commands, resumable terminal sessions, and patch-based edits. This extension brings that workflow to Pi while keeping Pi's runtime, sessions, project context, skills, and UI.
|
|
13
9
|
|
|
14
|
-
|
|
10
|
+
The point is to give the model tools it already knows how to use well: shell-first inspection, resumable command sessions, and large one-shot patch edits instead of piecemeal read/edit/write steps.
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pi install npm:@howaboua/pi-codex-conversion
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Development checkout
|
|
19
|
+
|
|
20
|
+
The Git checkout is mostly for development and mirrors the maintainer workflow. If you run it directly, you may need to build the bundled `apply_patch` binary for your platform.
|
|
21
|
+
|
|
22
|
+
Run the current checkout without installing globally:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pi --no-extensions --no-skills -e /path/to/pi-codex-conversion
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+

|
|
18
29
|
|
|
19
30
|
## Active tools in adapter mode
|
|
20
31
|
|
|
@@ -34,123 +45,32 @@ Notably:
|
|
|
34
45
|
- file creation and edits should default to `apply_patch`
|
|
35
46
|
- Pi may still expose additional runtime tools such as `parallel`; the prompt is written to tolerate that instead of assuming a fixed four-tool universe
|
|
36
47
|
|
|
37
|
-
##
|
|
48
|
+
## What changes in Pi
|
|
38
49
|
|
|
39
|
-
- `
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
- `
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
- `tests/` — deterministic unit tests
|
|
50
|
+
- Adapter mode activates automatically for OpenAI `gpt*` and `codex*` models, then restores the previous tool set when you switch away.
|
|
51
|
+
- Pi's composed prompt is preserved; the extension only adds a small Codex-style tool-use nudge.
|
|
52
|
+
- Shell activity is rendered with Codex-like labels such as `Ran`, `Explored`, `Read`, and background-terminal status.
|
|
53
|
+
- `apply_patch` renders as Codex-style `Added` / `Edited` / `Deleted` blocks, including inline partial-failure state.
|
|
54
|
+
- Native web search appears as a compact expandable summary after a turn, with queries and sources in the expanded view.
|
|
55
|
+
- Generated images are saved under `.pi/openai-codex-images/` at the workspace/repo root, with the latest image mirrored to `latest.png`.
|
|
46
56
|
|
|
47
|
-
##
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
npm run typecheck
|
|
51
|
-
npm test
|
|
52
|
-
npm run check
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Examples
|
|
57
|
+
## Command rendering examples
|
|
56
58
|
|
|
57
59
|
- `rg -n foo src` -> `Explored / Search foo in src`
|
|
58
60
|
- `rg --files src | head -n 50` -> `Explored / List src`
|
|
59
61
|
- `cat README.md` -> `Explored / Read README.md`
|
|
60
|
-
- `
|
|
61
|
-
-
|
|
62
|
-
- `write_stdin({ session_id, chars: "" })`
|
|
63
|
-
- `write_stdin({ session_id, chars: "y\\n" })` renders like `Interacted with background terminal`
|
|
64
|
-
- `view_image({ path: "/absolute/path/to/screenshot.png" })` is available on image-capable models
|
|
65
|
-
- `web_search` is surfaced only on `openai-codex`, and the adapter rewrites it into the native OpenAI Responses `type: "web_search"` payload instead of executing a local function tool
|
|
66
|
-
- `image_generation` is surfaced only on image-capable `openai-codex` models, and the adapter rewrites it into the native OpenAI Responses `type: "image_generation", output_format: "png"` payload instead of executing a local function tool
|
|
67
|
-
- native `image_generation` outputs are saved under `.pi/openai-codex-images/` at the workspace/repo root, with the newest image mirrored to `.pi/openai-codex-images/latest.png`
|
|
68
|
-
- when native web search is available, the adapter shows a one-time session notice and merged foldable search-activity messages instead of native tool-call rows
|
|
69
|
-
- `apply_patch` partial failures stay inline in the patch row so successful and failed file entries can be seen together
|
|
62
|
+
- `npm test` -> `Ran npm test`
|
|
63
|
+
- `write_stdin({ session_id, chars: "" })` -> `Waited for background terminal`
|
|
64
|
+
- `write_stdin({ session_id, chars: "y\n" })` -> `Interacted with background terminal`
|
|
70
65
|
|
|
71
66
|
Raw command output is still available by expanding the tool result.
|
|
72
67
|
|
|
73
|
-
##
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
pi install npm:@howaboua/pi-codex-conversion
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Local development:
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
pi install ./pi-codex-conversion
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Alternative Git install:
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
pi install git:github.com/IgorWarzocha/pi-codex-conversion
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Publishing
|
|
92
|
-
|
|
93
|
-
This package is already configured for public npm publishes via:
|
|
94
|
-
|
|
95
|
-
- `publishConfig.access = "public"`
|
|
96
|
-
- `prepublishOnly` / `prepack` checks
|
|
97
|
-
|
|
98
|
-
Useful commands:
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
npm run publish:dry-run
|
|
102
|
-
npm run publish:dev
|
|
103
|
-
npm run release:dev
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
What they do:
|
|
107
|
-
|
|
108
|
-
- `npm run publish:dry-run` — inspect what would be published
|
|
109
|
-
- `npm run publish:dev` — publish the current version under the `dev` dist-tag
|
|
110
|
-
- `npm run release:dev` — bump the package to the next `-dev.N` prerelease and publish it under the `dev` dist-tag
|
|
111
|
-
|
|
112
|
-
Typical flow:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
npm login
|
|
116
|
-
npm run publish:dry-run
|
|
117
|
-
npm run release:dev
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
For modern npm auth, just run `npm login` and complete the browser flow when prompted.
|
|
121
|
-
|
|
122
|
-
After publishing, install the dev build with:
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
pi install npm:@howaboua/pi-codex-conversion@dev
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
## Prompt behavior
|
|
129
|
-
|
|
130
|
-
The adapter does not build a standalone replacement prompt anymore. Instead it:
|
|
131
|
-
|
|
132
|
-
- keeps Pi's tool descriptions, Pi docs section, AGENTS/project context, skills inventory, and date/cwd when Pi already surfaced them
|
|
133
|
-
- adds the current shell to the transformed prompt so quoting and escaping can match the runtime environment
|
|
134
|
-
- rewrites the top-level role framing to Codex-style wording
|
|
135
|
-
- adds a small Codex delta to the existing `Guidelines` section
|
|
136
|
-
|
|
137
|
-
That keeps the prompt much closer to `pi-mono` while still steering the model toward Codex-style tool use.
|
|
138
|
-
|
|
139
|
-
## Notes
|
|
68
|
+
## Details worth knowing
|
|
140
69
|
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
- `
|
|
144
|
-
- `web_search`
|
|
145
|
-
- `image_generation` is exposed only for image-capable `openai-codex` models and is forwarded as the native OpenAI Codex Responses image-generation tool.
|
|
146
|
-
- generated images are written under `.pi/openai-codex-images/` at the workspace/repo root, and the latest image is mirrored to `.pi/openai-codex-images/latest.png`.
|
|
147
|
-
- `apply_patch` paths stay restricted to the current working directory.
|
|
148
|
-
- partial `apply_patch` failures stay in the original patch block and highlight the failed entry instead of adding a second warning row.
|
|
149
|
-
- `exec_command` / `write_stdin` use a custom PTY-backed session manager via `node-pty` for interactive sessions.
|
|
150
|
-
- tiny `exec_command` waits are clamped for non-interactive commands so short runs do not burn an avoidable follow-up tool call.
|
|
151
|
-
- empty `write_stdin` polls are clamped to a meaningful minimum wait so long-running processes are not repolled too aggressively.
|
|
152
|
-
- PTY output handling applies basic terminal rewrite semantics (`\r`, `\b`, erase-in-line, and common escape cleanup) so interactive redraws replay sensibly.
|
|
153
|
-
- Skills inventory is reintroduced in a Codex-style section when Pi's composed prompt already exposed the underlying Pi skills inventory.
|
|
70
|
+
- `exec_command` and `write_stdin` use a PTY-backed session manager for interactive commands and long-running processes.
|
|
71
|
+
- `apply_patch` accepts absolute paths as-is and resolves relative paths against the current working directory.
|
|
72
|
+
- Shell `apply_patch` is also available inside `exec_command`, but the dedicated `apply_patch` tool is preferred unless you are chaining edits with other shell steps.
|
|
73
|
+
- Native `web_search` and `image_generation` are forwarded to OpenAI Codex Responses tools rather than executed as local function tools.
|
|
154
74
|
|
|
155
75
|
## License
|
|
156
76
|
|
package/bin/apply_patch
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { chmodSync, existsSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
8
|
+
const platform = process.platform;
|
|
9
|
+
const arch = process.arch;
|
|
10
|
+
const exe = platform === "win32" ? "apply_patch.exe" : "apply_patch";
|
|
11
|
+
const platformArch = `${platform}-${arch}`;
|
|
12
|
+
const binary = join(root, "vendor", "apply-patch", platformArch, exe);
|
|
13
|
+
|
|
14
|
+
if (!existsSync(binary)) {
|
|
15
|
+
console.error(`apply_patch binary is not bundled for ${platformArch}.`);
|
|
16
|
+
console.error("Expected bundled binaries under vendor/apply-patch/<platform>-<arch>/.");
|
|
17
|
+
process.exit(127);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (platform !== "win32") {
|
|
21
|
+
try {
|
|
22
|
+
chmodSync(binary, 0o755);
|
|
23
|
+
} catch {
|
|
24
|
+
// Best effort. If chmod fails, spawn below will report the real error.
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const result = spawnSync(binary, process.argv.slice(2), {
|
|
29
|
+
stdio: "inherit",
|
|
30
|
+
env: process.env,
|
|
31
|
+
cwd: process.cwd(),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (result.error) {
|
|
35
|
+
console.error(result.error.message);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
process.exit(result.status ?? 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@howaboua/pi-codex-conversion",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.5.0-dev.10.fab478e",
|
|
4
4
|
"description": "Codex-oriented tool and prompt adapter for pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -21,10 +21,6 @@
|
|
|
21
21
|
"apply-patch"
|
|
22
22
|
],
|
|
23
23
|
"license": "MIT",
|
|
24
|
-
"os": [
|
|
25
|
-
"darwin",
|
|
26
|
-
"linux"
|
|
27
|
-
],
|
|
28
24
|
"pi": {
|
|
29
25
|
"extensions": [
|
|
30
26
|
"./src/index.ts"
|
|
@@ -33,6 +29,9 @@
|
|
|
33
29
|
"files": [
|
|
34
30
|
"src/**/*.ts",
|
|
35
31
|
"src/**/*.md",
|
|
32
|
+
"bin/apply_patch",
|
|
33
|
+
"bin/apply_patch.cmd",
|
|
34
|
+
"vendor/apply-patch/**",
|
|
36
35
|
"available-tools.png",
|
|
37
36
|
"README.md",
|
|
38
37
|
"LICENSE"
|
|
@@ -45,16 +44,19 @@
|
|
|
45
44
|
"publish:dev": "npm publish --tag dev",
|
|
46
45
|
"release:dev": "npm version prerelease --preid dev && npm publish --tag dev",
|
|
47
46
|
"prepack": "npm run check",
|
|
48
|
-
"prepublishOnly": "npm run
|
|
47
|
+
"prepublishOnly": "npm run verify:apply-patch-binaries",
|
|
48
|
+
"build:apply-patch": "node scripts/build-apply-patch-binary.mjs",
|
|
49
|
+
"sync:apply-patch-source": "node scripts/sync-apply-patch-source.mjs",
|
|
50
|
+
"verify:apply-patch-binaries": "node scripts/verify-apply-patch-binaries.mjs"
|
|
49
51
|
},
|
|
50
52
|
"publishConfig": {
|
|
51
53
|
"access": "public"
|
|
52
54
|
},
|
|
53
55
|
"peerDependencies": {
|
|
54
|
-
"@earendil-works/pi-ai": "
|
|
55
|
-
"@earendil-works/pi-coding-agent": "
|
|
56
|
-
"@earendil-works/pi-tui": "
|
|
57
|
-
"typebox": "
|
|
56
|
+
"@earendil-works/pi-ai": "*",
|
|
57
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
58
|
+
"@earendil-works/pi-tui": "*",
|
|
59
|
+
"typebox": "*"
|
|
58
60
|
},
|
|
59
61
|
"devDependencies": {
|
|
60
62
|
"@earendil-works/pi-ai": "^0.74.0",
|
|
@@ -69,5 +71,8 @@
|
|
|
69
71
|
"partial-json": "^0.1.7",
|
|
70
72
|
"tree-sitter-bash": "^0.25.1",
|
|
71
73
|
"web-tree-sitter": "^0.26.7"
|
|
74
|
+
},
|
|
75
|
+
"bin": {
|
|
76
|
+
"apply_patch": "./bin/apply_patch"
|
|
72
77
|
}
|
|
73
78
|
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
WEB_SEARCH_SESSION_NOTE_TYPE,
|
|
30
30
|
} from "./tools/web-search-tool.ts";
|
|
31
31
|
import { registerWriteStdinTool } from "./tools/write-stdin-tool.ts";
|
|
32
|
+
import { ensureBundledApplyPatchOnPath } from "./tools/apply-patch-binary.ts";
|
|
32
33
|
|
|
33
34
|
interface AdapterState {
|
|
34
35
|
enabled: boolean;
|
|
@@ -57,6 +58,7 @@ function isToolCallOnlyAssistantMessage(message: unknown): boolean {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
export default function codexConversion(pi: ExtensionAPI) {
|
|
61
|
+
ensureBundledApplyPatchOnPath();
|
|
60
62
|
const tracker = createExecCommandTracker();
|
|
61
63
|
const state: AdapterState = { enabled: false, cwd: process.cwd(), promptSkills: [] };
|
|
62
64
|
const sessions = createExecSessionManager();
|
|
@@ -163,8 +165,9 @@ function enableAdapter(pi: ExtensionAPI, ctx: ExtensionContext, state: AdapterSt
|
|
|
163
165
|
const toolNames = mergeAdapterTools(pi.getActiveTools(), getAdapterToolNames(ctx));
|
|
164
166
|
if (!state.enabled) {
|
|
165
167
|
// Preserve the previous active set once so switching away from Codex-like
|
|
166
|
-
// models restores the user's existing Pi tool configuration.
|
|
167
|
-
|
|
168
|
+
// models restores the user's existing Pi tool configuration. Strip adapter
|
|
169
|
+
// tools in case a fresh session starts from persisted/mixed active tools.
|
|
170
|
+
state.previousToolNames = stripAdapterTools(pi.getActiveTools());
|
|
168
171
|
state.enabled = true;
|
|
169
172
|
}
|
|
170
173
|
pi.setActiveTools(toolNames);
|
|
@@ -208,7 +211,7 @@ export function mergeAdapterTools(activeTools: string[], adapterTools: string[])
|
|
|
208
211
|
}
|
|
209
212
|
|
|
210
213
|
export function restoreTools(previousTools: string[], activeTools: string[]): string[] {
|
|
211
|
-
const restored =
|
|
214
|
+
const restored = stripAdapterTools(previousTools);
|
|
212
215
|
for (const toolName of activeTools) {
|
|
213
216
|
if (!ADAPTER_TOOL_NAMES.includes(toolName) && !restored.includes(toolName)) {
|
|
214
217
|
restored.push(toolName);
|
|
@@ -217,6 +220,10 @@ export function restoreTools(previousTools: string[], activeTools: string[]): st
|
|
|
217
220
|
return restored;
|
|
218
221
|
}
|
|
219
222
|
|
|
223
|
+
export function stripAdapterTools(toolNames: string[]): string[] {
|
|
224
|
+
return toolNames.filter((toolName) => !ADAPTER_TOOL_NAMES.includes(toolName));
|
|
225
|
+
}
|
|
226
|
+
|
|
220
227
|
function hasAdapterTools(activeTools: string[]): boolean {
|
|
221
228
|
return activeTools.some((toolName) => ADAPTER_TOOL_NAMES.includes(toolName));
|
|
222
229
|
}
|
package/src/patch/paths.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, isAbsolute,
|
|
2
|
+
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
3
|
import { DiffError } from "./types.ts";
|
|
4
4
|
|
|
5
5
|
export function normalizePatchPath({ path }: { path: string }): string {
|
|
@@ -8,20 +8,15 @@ export function normalizePatchPath({ path }: { path: string }): string {
|
|
|
8
8
|
return withoutAt.replace(/^['"]|['"]$/g, "");
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
//
|
|
11
|
+
// Match Codex apply_patch path handling: absolute patch paths are accepted
|
|
12
|
+
// as-is, while relative paths are resolved against ctx.cwd.
|
|
13
13
|
export function resolvePatchPath({ cwd, patchPath }: { cwd: string; patchPath: string }): string {
|
|
14
14
|
const normalized = normalizePatchPath({ path: patchPath });
|
|
15
15
|
if (!normalized) {
|
|
16
16
|
throw new DiffError("Patch path cannot be empty");
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
const rel = relative(cwd, absolutePath);
|
|
21
|
-
if (!isAbsolute(normalized) && (rel.startsWith("..") || isAbsolute(rel))) {
|
|
22
|
-
throw new DiffError(`Path escapes working directory: ${normalized}`);
|
|
23
|
-
}
|
|
24
|
-
return absolutePath;
|
|
19
|
+
return isAbsolute(normalized) ? normalized : resolve(cwd, normalized);
|
|
25
20
|
}
|
|
26
21
|
|
|
27
22
|
export function openFileAtPath({ cwd, path }: { cwd: string; path: string }): string {
|
|
@@ -5,15 +5,11 @@ export interface PromptSkill {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
const CODEX_GUIDELINES = [
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"Use `
|
|
12
|
-
"
|
|
13
|
-
"For short or non-interactive commands, prefer the default `exec_command` wait instead of a tiny `yield_time_ms` that forces an extra follow-up call.",
|
|
14
|
-
"When polling a running exec session with empty `chars`, wait meaningfully between polls and do not repeatedly poll by reflex.",
|
|
15
|
-
"Do not request `tty` unless interactive terminal behavior is required.",
|
|
16
|
-
"Native `image_generation` outputs are saved under `.pi/openai-codex-images/` and mirrored to `.pi/openai-codex-images/latest.png`. Use `view_image` only when pixel-level inspection is necessary.",
|
|
8
|
+
"Use `exec_command` for shell commands, file inspection, builds, and tests; prefer `rg` / `rg --files` for discovery and focused commands over truncation.",
|
|
9
|
+
"Use `apply_patch` for text-file changes, including creates/deletes/moves; group related multi-file edits into one patch.",
|
|
10
|
+
"Prefer the `apply_patch` tool; use shell `apply_patch` only when chaining edits with other shell steps.",
|
|
11
|
+
"Use `write_stdin` only for running `exec_command` sessions; poll sparingly.",
|
|
12
|
+
"Run independent tool calls in parallel when practical.",
|
|
17
13
|
];
|
|
18
14
|
|
|
19
15
|
function insertBeforeTrailingContext(prompt: string, section: string): string {
|
|
@@ -88,9 +84,9 @@ function injectSkills(prompt: string, skills: PromptSkill[]): string {
|
|
|
88
84
|
}
|
|
89
85
|
|
|
90
86
|
function injectGuidelines(prompt: string): string {
|
|
91
|
-
const match = prompt.match(/(^Guidelines:\n)([\s\S]*?)(\n\n(
|
|
87
|
+
const match = prompt.match(/(^Guidelines:\n)([\s\S]*?)(\n\n(?=Pi documentation\b|# Project Context|# Skills|Current date:))/m);
|
|
92
88
|
if (!match || match.index === undefined) {
|
|
93
|
-
const fallbackSection = `
|
|
89
|
+
const fallbackSection = `Guidelines:\n${CODEX_GUIDELINES.map((line) => `- ${line}`).join("\n")}`;
|
|
94
90
|
return insertBeforeTrailingContext(prompt, fallbackSection);
|
|
95
91
|
}
|
|
96
92
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, delimiter, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
export function getBundledApplyPatchBinDir(): string {
|
|
6
|
+
return join(dirname(dirname(dirname(fileURLToPath(import.meta.url)))), "bin");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function ensureBundledApplyPatchOnPath(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
10
|
+
const binDir = getBundledApplyPatchBinDir();
|
|
11
|
+
const wrapperPath = join(binDir, process.platform === "win32" ? "apply_patch.cmd" : "apply_patch");
|
|
12
|
+
if (!existsSync(wrapperPath)) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const currentPath = env.PATH ?? "";
|
|
16
|
+
const entries = currentPath.split(delimiter).filter(Boolean);
|
|
17
|
+
if (!entries.includes(binDir)) {
|
|
18
|
+
env.PATH = [binDir, ...entries].join(delimiter);
|
|
19
|
+
}
|
|
20
|
+
return binDir;
|
|
21
|
+
}
|
|
@@ -280,12 +280,8 @@ export function registerApplyPatchTool(pi: ExtensionAPI): void {
|
|
|
280
280
|
pi.registerTool({
|
|
281
281
|
name: "apply_patch",
|
|
282
282
|
label: "apply_patch",
|
|
283
|
-
description: "
|
|
283
|
+
description: "Apply a patch to create, edit, delete, or move files.",
|
|
284
284
|
promptSnippet: "Edit files with a patch.",
|
|
285
|
-
promptGuidelines: [
|
|
286
|
-
"Prefer apply_patch for focused textual edits instead of rewriting whole files.",
|
|
287
|
-
"When one task needs coordinated edits across multiple files, send them in a single apply_patch call when one coherent patch will do.",
|
|
288
|
-
],
|
|
289
285
|
parameters: APPLY_PATCH_PARAMETERS,
|
|
290
286
|
prepareArguments: prepareApplyPatchArguments,
|
|
291
287
|
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -8,16 +8,16 @@ import { formatUnifiedExecResult } from "./unified-exec-format.ts";
|
|
|
8
8
|
|
|
9
9
|
const EXEC_COMMAND_PARAMETERS = Type.Object({
|
|
10
10
|
cmd: Type.String({ description: "Shell command to execute." }),
|
|
11
|
-
workdir: Type.Optional(Type.String({ description: "
|
|
12
|
-
shell: Type.Optional(Type.String({ description: "
|
|
11
|
+
workdir: Type.Optional(Type.String({ description: "Defaults to current cwd." })),
|
|
12
|
+
shell: Type.Optional(Type.String({ description: "Defaults to the user's shell." })),
|
|
13
13
|
tty: Type.Optional(
|
|
14
14
|
Type.Boolean({
|
|
15
|
-
description: "
|
|
15
|
+
description: "Allocate a TTY. Defaults to false.",
|
|
16
16
|
}),
|
|
17
17
|
),
|
|
18
|
-
yield_time_ms: Type.Optional(Type.Number({ description: "
|
|
19
|
-
max_output_tokens: Type.Optional(Type.Number({ description: "
|
|
20
|
-
login: Type.Optional(Type.Boolean({ description: "Whether to run
|
|
18
|
+
yield_time_ms: Type.Optional(Type.Number({ description: "Wait for output before yielding." })),
|
|
19
|
+
max_output_tokens: Type.Optional(Type.Number({ description: "Excess output will be truncated." })),
|
|
20
|
+
login: Type.Optional(Type.Boolean({ description: "Whether to run through a login-style shell so user PATH/toolchain setup is loaded. Defaults to true." })),
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
interface ExecCommandParams {
|
|
@@ -136,14 +136,8 @@ export function registerExecCommandTool(pi: ExtensionAPI, tracker: ExecCommandTr
|
|
|
136
136
|
pi.registerTool({
|
|
137
137
|
name: "exec_command",
|
|
138
138
|
label: "exec_command",
|
|
139
|
-
description: "Runs a command
|
|
139
|
+
description: "Runs a shell command, returning output or a session ID for ongoing interaction.",
|
|
140
140
|
promptSnippet: "Run a command.",
|
|
141
|
-
promptGuidelines: [
|
|
142
|
-
"Use exec_command for search, listing files, and local text-file reads.",
|
|
143
|
-
"Prefer rg or rg --files when possible.",
|
|
144
|
-
"For short or non-interactive commands, omit `yield_time_ms` so the default wait can avoid unnecessary follow-up calls.",
|
|
145
|
-
"Keep tty disabled unless the command truly needs interactive terminal behavior.",
|
|
146
|
-
],
|
|
147
141
|
parameters: EXEC_COMMAND_PARAMETERS,
|
|
148
142
|
prepareArguments: prepareExecCommandArguments,
|
|
149
143
|
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -79,7 +79,7 @@ export function rewriteNativeImageGenerationTool(payload: unknown, model: Extens
|
|
|
79
79
|
|
|
80
80
|
export function createImageGenerationTool(): ToolDefinition<typeof IMAGE_GENERATION_PARAMETERS> {
|
|
81
81
|
const description =
|
|
82
|
-
"Generate an image.
|
|
82
|
+
"Generate an image. Outputs are saved under `.pi/openai-codex-images/` and mirrored to `.pi/openai-codex-images/latest.png`.";
|
|
83
83
|
return {
|
|
84
84
|
name: "image_generation",
|
|
85
85
|
label: "image_generation",
|
|
@@ -12,7 +12,7 @@ import { Text } from "@earendil-works/pi-tui";
|
|
|
12
12
|
|
|
13
13
|
const VIEW_IMAGE_UNSUPPORTED_MESSAGE = "view_image is not allowed because you do not support image inputs";
|
|
14
14
|
const DETAIL_DESCRIPTION =
|
|
15
|
-
"
|
|
15
|
+
"Use `original` to preserve the file's original resolution; omit for default resized behavior.";
|
|
16
16
|
|
|
17
17
|
interface ViewImageParams {
|
|
18
18
|
path: string;
|
|
@@ -37,7 +37,7 @@ type ViewImageParameters = ReturnType<typeof createViewImageParameters>;
|
|
|
37
37
|
|
|
38
38
|
function createViewImageParameters(allowOriginalDetail: boolean) {
|
|
39
39
|
const properties: Record<string, TSchema> = {
|
|
40
|
-
path: Type.String({ description: "Local
|
|
40
|
+
path: Type.String({ description: "Local image file path." }),
|
|
41
41
|
};
|
|
42
42
|
if (allowOriginalDetail) {
|
|
43
43
|
properties.detail = Type.Optional(Type.String({ description: DETAIL_DESCRIPTION }));
|
|
@@ -143,10 +143,8 @@ export function createViewImageTool(options: CreateViewImageToolOptions = {}): T
|
|
|
143
143
|
return {
|
|
144
144
|
name: "view_image",
|
|
145
145
|
label: "view_image",
|
|
146
|
-
description:
|
|
147
|
-
"View a local image from the filesystem (only use if given a full filepath by the user, and the image isn't already attached to the thread context within <image ...> tags).",
|
|
146
|
+
description: "View a local image file.",
|
|
148
147
|
promptSnippet: "View a local image from the filesystem.",
|
|
149
|
-
promptGuidelines: ["Use view_image only for image files. Use exec_command for text-file inspection."],
|
|
150
148
|
parameters,
|
|
151
149
|
prepareArguments: prepareViewImageArguments,
|
|
152
150
|
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -6,10 +6,10 @@ import type { ExecSessionManager, UnifiedExecResult } from "./exec-session-manag
|
|
|
6
6
|
import { formatUnifiedExecResult } from "./unified-exec-format.ts";
|
|
7
7
|
|
|
8
8
|
const WRITE_STDIN_PARAMETERS = Type.Object({
|
|
9
|
-
session_id: Type.Number({ description: "
|
|
10
|
-
chars: Type.Optional(Type.String({ description: "Bytes to write
|
|
11
|
-
yield_time_ms: Type.Optional(Type.Number({ description: "
|
|
12
|
-
max_output_tokens: Type.Optional(Type.Number({ description: "
|
|
9
|
+
session_id: Type.Number({ description: "Running exec session ID." }),
|
|
10
|
+
chars: Type.Optional(Type.String({ description: "Bytes to write. Empty polls." })),
|
|
11
|
+
yield_time_ms: Type.Optional(Type.Number({ description: "Wait for output before yielding." })),
|
|
12
|
+
max_output_tokens: Type.Optional(Type.Number({ description: "Excess output will be truncated." })),
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
interface WriteStdinParams {
|
|
@@ -108,12 +108,8 @@ export function registerWriteStdinTool(pi: ExtensionAPI, sessions: ExecSessionMa
|
|
|
108
108
|
pi.registerTool({
|
|
109
109
|
name: "write_stdin",
|
|
110
110
|
label: "write_stdin",
|
|
111
|
-
description: "Writes
|
|
111
|
+
description: "Writes to or polls a running exec session.",
|
|
112
112
|
promptSnippet: "Write to an exec session.",
|
|
113
|
-
promptGuidelines: [
|
|
114
|
-
"Use empty `chars` only to poll a running exec session.",
|
|
115
|
-
"When polling with empty `chars`, wait meaningfully between polls and do not repeatedly poll by reflex.",
|
|
116
|
-
],
|
|
117
113
|
parameters: WRITE_STDIN_PARAMETERS,
|
|
118
114
|
async execute(_toolCallId, params) {
|
|
119
115
|
const typed = parseWriteStdinParams(params);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|