pi-repl-py 0.3.0 → 0.4.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/index.ts CHANGED
@@ -4,6 +4,7 @@ import { basename, join } from "node:path";
4
4
  import { homedir } from "node:os";
5
5
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
6
6
  import { Type } from "typebox";
7
+ import { withSkillsBlock } from "./src/extension/skill-hook.js";
7
8
  import { EngineManager } from "./src/engine/index.js";
8
9
  import { ExecuteCellComponent, type ExecuteDetails, type ExecuteRenderState } from "./src/extension/render.js";
9
10
  import { EngineLifecycle } from "./src/extension/session-engine.js";
@@ -109,6 +110,16 @@ export default function (pi: ExtensionAPI) {
109
110
  return { content: event.content, details: stashed.details, isError: true };
110
111
  });
111
112
 
113
+ // --- pi gates skills on the read tool (absent in repl); re-emit them via withSkillsBlock. ---
114
+ pi.on("before_agent_start", (event) => {
115
+ if (!active()) return;
116
+ const systemPrompt = withSkillsBlock(
117
+ event.systemPrompt,
118
+ event.systemPromptOptions?.skills ?? [],
119
+ );
120
+ return systemPrompt === undefined ? undefined : { systemPrompt };
121
+ });
122
+
112
123
  pi.registerTool<typeof executeSchema, ExecuteDetails, Partial<ExecuteRenderState>>({
113
124
  name: "execute",
114
125
  label: "execute",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-repl-py",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "A pi extension with a single tool: execute, running a TypeScript host with a persistent Python (ipykernel) evaluator and a user-configurable toolbox of functions.",
6
6
  "keywords": [
@@ -6,14 +6,17 @@
6
6
  // more signal; the machine reads every line every turn.
7
7
 
8
8
  export const executeToolDescription =
9
- "You have one tool: a real `ipython` kernel that stays alive across cells and turns. " +
10
- "This persistent Python workspace is your only surface it does the work of bash, read, write, edit, " +
11
- "search, and file handling, and everything you define (variables, imports, helpers loaded from " +
12
- "`~/.pi/agent/pi-repl/helpers/`) survives for reuse in later cells. A cell returns its final expression; " +
13
- "printed output is captured separately.";
9
+ "Execute Python cells in a persistent ipython kernel that stays alive across cells and turns. " +
10
+ "It replaces the default read, bash, edit, write, and search tools file work, shell commands, and " +
11
+ "searches all run as Python. Everything you define (variables, imports, and helpers preloaded into the " +
12
+ "workspace namespace) survives for reuse in later cells. A cell returns its final expression; printed " +
13
+ "output is captured separately. Oversized output is truncated: 1,000,000 characters per cell, 4,096 per " +
14
+ "line. Reads are expensive — every printed value enters the context, so hold artifacts in variables, " +
15
+ "parse before printing, and print only the small bounded slice the next decision needs. Keep cells lean; " +
16
+ "full-file dumps and raw result lists bloat the conversation.";
14
17
 
15
18
  export const executePromptSnippet =
16
- "The persistent Python workspace is your only tool: keep artifacts in variables across cells for reuse, use the loaded helpers, prefer surgical reads/edits over full-file dumps and rewrites, and parse before you print so context stays lean.";
19
+ "Execute Python cells in a persistent ipython kernel (replaces read, bash, edit, write, and search; state survives across cells and turns)";
17
20
 
18
21
  // --- the workspace doctrine riding the execute tool ---
19
22
  export function buildPromptGuidelines(preloaded: string[]): string[] {
@@ -45,7 +48,7 @@ export function buildPromptGuidelines(preloaded: string[]): string[] {
45
48
  ...(preloaded.length
46
49
  ? [
47
50
  "## Helpers",
48
- "These helpers are given to you by the user to use directly (loaded from `~/.pi/agent/pi-repl/helpers/`). Descriptions appear below.",
51
+ "These helpers are already defined in the workspace namespace. Use them by name as you would any other loaded function, class, or variable. Their code already executed at kernel boot. Descriptions appear below.",
49
52
  "",
50
53
  ...preloaded,
51
54
  "",
@@ -0,0 +1,22 @@
1
+ // --- skills cannot reach the prompt in --repl: pi gates <available_skills> on the read tool
2
+ // (hasRead), which repl doesn't have. Re-emit them with pi's own formatter in pi's slot. ---
3
+ import { formatSkillsForPrompt, type Skill } from "@mariozechner/pi-coding-agent";
4
+
5
+ const CWD_MARKER = "\nCurrent working directory:"; // skills sit just before this, pi's last line
6
+ const READ_LINE = "Use the read tool to load a skill's file when the task matches its description."; // canon line
7
+ const EXECUTE_LINE = "Load a skill's SKILL.md file contents via execute (read the file with Python)."; // repl has no read
8
+
9
+ /** The prompt with the skills block in pi's slot; undefined if nothing should change. */
10
+ export function withSkillsBlock(
11
+ prompt: string,
12
+ skills: Skill[],
13
+ alreadyPresent = prompt.includes("<available_skills>"),
14
+ ): string | undefined {
15
+ if (skills.length === 0) return undefined;
16
+ let extra = formatSkillsForPrompt(skills);
17
+ if (!extra) return undefined;
18
+ if (alreadyPresent) return undefined;
19
+ extra = extra.replace(READ_LINE, EXECUTE_LINE);
20
+ const idx = prompt.indexOf(CWD_MARKER);
21
+ return idx === -1 ? prompt + extra : prompt.slice(0, idx) + extra + prompt.slice(idx);
22
+ }