pi-repl-py 0.1.1 → 0.2.1
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 +22 -22
- package/docs/ARCHITECTURE.md +128 -88
- package/docs/helpers.md +110 -0
- package/docs/philosophy.md +67 -59
- package/index.ts +4 -22
- package/package.json +5 -6
- package/scripts/setup-venv.mjs +39 -17
- package/src/engine/index.ts +90 -424
- package/src/engine/kernel.ts +598 -0
- package/src/engine/session.ts +149 -0
- package/src/engine/zmtp.ts +251 -0
- package/src/extension/helpers.ts +46 -0
- package/src/extension/preview/candidates.ts +7 -53
- package/src/extension/preview/descriptor.ts +2 -2
- package/src/extension/preview/index.ts +3 -11
- package/src/extension/preview/shell.ts +4 -4
- package/src/extension/preview/types.ts +1 -1
- package/src/extension/prompt.ts +67 -45
- package/src/extension/render-core.ts +5 -9
- package/src/extension/render.ts +2 -11
- package/src/extension/session-engine.ts +9 -31
- package/src/extension/tool-meta.ts +6 -6
- package/docs/how-to-functions.md +0 -108
- package/src/engine/guest.py +0 -320
- package/src/engine/protocol.ts +0 -66
- package/src/engine/toolbox/bash.py +0 -72
- package/src/engine/toolbox/edit.py +0 -37
- package/src/engine/toolbox/read.py +0 -26
- package/src/engine/toolbox/write.py +0 -23
- package/src/extension/config.ts +0 -64
- package/src/extension/preview-core.ts +0 -2
- package/src/extension/toolbox.ts +0 -98
package/src/extension/prompt.ts
CHANGED
|
@@ -1,54 +1,76 @@
|
|
|
1
|
-
// --- prompt: the execute tool's model-facing contract (pure, no pi/
|
|
2
|
-
// --- mirrors pi-robust-edit's schema/domain split: content lives here; the thin adapter in tool-meta wires it in ---
|
|
1
|
+
// --- prompt: the execute tool's model-facing contract (pure, no pi/helper dep) ---
|
|
3
2
|
|
|
4
3
|
export const executeToolDescription =
|
|
5
|
-
"
|
|
6
|
-
"functions, and data survive across
|
|
7
|
-
"
|
|
8
|
-
"
|
|
4
|
+
"You have one tool: a persistent Python workspace backed by a real `ipython` kernel. " +
|
|
5
|
+
"Variables, imports, functions, and data survive across cells and turns. Use this tool to read files, " +
|
|
6
|
+
"run shell commands, search code, transform data, and build up solutions — all inside Python. " +
|
|
7
|
+
"Helpers in `~/.pi/agent/pi-repl/helpers/` are loaded at boot as functions; see what's loaded with " +
|
|
8
|
+
"`[k for k in globals() if not k.startswith('_')]`. A cell returns its final expression; printed output " +
|
|
9
|
+
"is captured separately.";
|
|
9
10
|
|
|
10
11
|
export const executePromptSnippet =
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"ls() lists them, help(name) shows usage";
|
|
12
|
+
"Use the Python workspace: keep state in variables, batch independent reads/searches in one cell, " +
|
|
13
|
+
"edit files safely, and iterate in small cells.";
|
|
14
14
|
|
|
15
|
-
// --- the
|
|
15
|
+
// --- the workspace doctrine riding the execute tool ---
|
|
16
16
|
export function buildPromptGuidelines(preloaded: string[]): string[] {
|
|
17
17
|
return [
|
|
18
|
-
"##
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"",
|
|
22
|
-
"##
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
""
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
""
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
18
|
+
"## This workspace is your only tool",
|
|
19
|
+
"In `--repl` mode, `execute` is the only callable tool. Read files, run shell, search, and edit " +
|
|
20
|
+
"all happen inside Python.",
|
|
21
|
+
"",
|
|
22
|
+
"## The loop is generate → execute → observe → iterate",
|
|
23
|
+
"Write a cell, run it, observe the result, then write the next cell. Build solutions incrementally.",
|
|
24
|
+
"",
|
|
25
|
+
"## State persists",
|
|
26
|
+
"Variables, imports, and functions survive across cells and turns. Assign read/search results to " +
|
|
27
|
+
"named variables and reuse them.",
|
|
28
|
+
"",
|
|
29
|
+
"## Chain big tasks into verifiable steps",
|
|
30
|
+
"Break ambitious requests into independently checkable steps. Confirm assumptions before writing " +
|
|
31
|
+
"code that depends on them.",
|
|
32
|
+
"",
|
|
33
|
+
"## Shell & files are plain Python",
|
|
34
|
+
"`!cmd` / `%%bash` for fire-and-forget shell; `subprocess.run(..., timeout=...)` when you need the result back " +
|
|
35
|
+
"as a value — always set a `timeout` on anything that could hang (the evaluator does not kill a " +
|
|
36
|
+
"silent cell automatically). `open()` / `pathlib` read and write files. For safe edits: read the full " +
|
|
37
|
+
"file, modify in memory, write once, then re-read to verify.",
|
|
38
|
+
"",
|
|
39
|
+
"## Batch independent work, keep exploratory cells small",
|
|
40
|
+
"Batch independent reads, searches, and setup steps in one cell to reduce round-trips. Keep " +
|
|
41
|
+
"exploratory/iterative cells small so you can observe and adjust.",
|
|
42
|
+
"",
|
|
43
|
+
"## Search efficiently",
|
|
44
|
+
"Use `rg`, `fd`, `grep`, `find` via `subprocess.run` for deep searches, not Python loops.",
|
|
45
|
+
"",
|
|
46
|
+
...(preloaded.length
|
|
47
|
+
? [
|
|
48
|
+
"## Helpers",
|
|
49
|
+
"User helpers load from `~/.pi/agent/pi-repl/helpers/`. Their descriptions appear below. " +
|
|
50
|
+
"List what's loaded with `[k for k in globals() if not k.startswith('_')]`.",
|
|
51
|
+
"",
|
|
52
|
+
...preloaded,
|
|
53
|
+
"",
|
|
54
|
+
]
|
|
55
|
+
: []),
|
|
56
|
+
"## Compose and reuse",
|
|
57
|
+
"If the same pattern appears more than once, wrap it in a `def` and reuse it.",
|
|
58
|
+
"",
|
|
59
|
+
"## Output discipline",
|
|
60
|
+
"Printing is a context cost: everything a cell prints stays in the transcript. Print slices, " +
|
|
61
|
+
"counts, and summaries. Keep large values in variables. End a cell with `;` to suppress the " +
|
|
62
|
+
"last-expression echo.",
|
|
63
|
+
"",
|
|
64
|
+
"## Environment boundary",
|
|
65
|
+
"The evaluator runs in a project-local venv, not the system Python. Do not install a target project's " +
|
|
66
|
+
"dependencies into the evaluator just to make that project run there. Run external projects " +
|
|
67
|
+
"through their own interface and normal commands.",
|
|
68
|
+
"",
|
|
69
|
+
"## Engine reset guard",
|
|
70
|
+
"If the output begins with `<repl_engine_reset>`, the kernel was rebuilt from the last snapshot. " +
|
|
71
|
+
"Some variables may be revived, some lost, and anything defined after the snapshot is gone. " +
|
|
72
|
+
"Re-verify variables before reusing them — never interpolate a restored variable into a shell " +
|
|
73
|
+
"command until you have confirmed it still holds what you expect. Functions, classes, and live " +
|
|
74
|
+
"handles cannot be snapshotted and must be redefined.",
|
|
53
75
|
];
|
|
54
76
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/** Pure layout, free of pi imports so unit tests drive it directly. */
|
|
2
2
|
|
|
3
3
|
export interface ExecuteDetails {
|
|
4
4
|
status?: "ok" | "error" | "aborted" | string;
|
|
@@ -21,7 +21,7 @@ export interface ExecuteRenderState {
|
|
|
21
21
|
hasResult: boolean;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
import { previewCell } from "./preview
|
|
24
|
+
import { previewCell } from "./preview/index.js";
|
|
25
25
|
|
|
26
26
|
export type StatusKind = "error" | "aborted" | "running" | "queued" | "done";
|
|
27
27
|
export type BgKind = "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
|
|
@@ -49,11 +49,7 @@ export function formatDuration(durationMs: number | undefined): string | undefin
|
|
|
49
49
|
|
|
50
50
|
const SGR_PATTERN = /\x1b\[([0-9;]*)m/g;
|
|
51
51
|
|
|
52
|
-
/**
|
|
53
|
-
* Append a reset when `line` ends with a foreground or background color still
|
|
54
|
-
* open, so a span that wrapping split across lines cannot bleed into the
|
|
55
|
-
* trailing padding or the next row.
|
|
56
|
-
*/
|
|
52
|
+
/** Close an open color so a line wrapped across words can't bleed into padding or the next row. */
|
|
57
53
|
export function closeOpenSgr(line: string): string {
|
|
58
54
|
let fgOpen = false;
|
|
59
55
|
let bgOpen = false;
|
|
@@ -134,7 +130,7 @@ function outputText(state: ExecuteRenderState): string {
|
|
|
134
130
|
function topLine(state: ExecuteRenderState, width: number, deps: RenderDeps): string {
|
|
135
131
|
const code = state.code.trimEnd();
|
|
136
132
|
const preview = previewCell(code);
|
|
137
|
-
const language = preview.kind === "shell" ? "repl · shell" :
|
|
133
|
+
const language = preview.kind === "shell" ? "repl · shell" : "repl";
|
|
138
134
|
const prefix = `${marker(state, deps)} ${deps.fg("muted", language)}`;
|
|
139
135
|
|
|
140
136
|
// --- suffix priority: expand hint > error > duration > counts, so truncation never hides the expand key ---
|
|
@@ -168,7 +164,7 @@ function topLine(state: ExecuteRenderState, width: number, deps: RenderDeps): st
|
|
|
168
164
|
// --- budget: width minus leading space, prefix, suffix, and separators ---
|
|
169
165
|
const fixed = 1 + deps.visibleWidth(prefix) + separatorWidth + deps.visibleWidth(suffix);
|
|
170
166
|
const previewBudget = Math.max(8, width - fixed - separatorWidth);
|
|
171
|
-
// --- a semantic preview is a one-line summary; highlight Python code, accent shell
|
|
167
|
+
// --- a semantic preview is a one-line summary; highlight Python code, accent shell intent ---
|
|
172
168
|
let middle = "";
|
|
173
169
|
if (preview.text) {
|
|
174
170
|
const previewText =
|
package/src/extension/render.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TUI adapter for the `execute` cell renderer.
|
|
3
|
-
*
|
|
4
|
-
* Binds pi's theme, syntax highlighting, key hints, and width primitives to the
|
|
5
|
-
* pure layout in render-core.ts, which is unit-tested outside pi's runtime.
|
|
6
|
-
*/
|
|
1
|
+
/** TUI adapter binding pi's theme/width to the unit-tested pure layout in render-core.ts. */
|
|
7
2
|
|
|
8
3
|
import { highlightCode, keyHint, keyText, rawKeyHint, type Theme } from "@mariozechner/pi-coding-agent";
|
|
9
4
|
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
|
|
@@ -35,11 +30,7 @@ function makeDeps(theme: Theme): RenderDeps {
|
|
|
35
30
|
};
|
|
36
31
|
}
|
|
37
32
|
|
|
38
|
-
/**
|
|
39
|
-
* The layout of a cell only changes when its state or the spinner frame does,
|
|
40
|
-
* but the TUI repaints on every frame. Rendering from a key of both stops the
|
|
41
|
-
* recompute-per-frame (and with it, flicker on wide panes).
|
|
42
|
-
*/
|
|
33
|
+
/** The layout only changes on state/spinner change, but the TUI repaints every frame; key by both to avoid recompute flicker. */
|
|
43
34
|
function renderVersion(state: ExecuteRenderState): string {
|
|
44
35
|
const details = state.details ? JSON.stringify(state.details) : "";
|
|
45
36
|
return [
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
//
|
|
1
|
+
// revival is part of create() so a session that gets teardown without a session_start reload still revives
|
|
2
2
|
|
|
3
3
|
import type { RestoreResult } from "../engine/index.js";
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* A revived session can carry hundreds of variables; listing them all turns
|
|
7
|
-
* the banner and the reset notice into a wall. Show enough to orient, then
|
|
8
|
-
* count the rest.
|
|
9
|
-
*/
|
|
5
|
+
/** Show enough names to orient, then count the rest (a revive can carry hundreds). */
|
|
10
6
|
export function summarizeNames(names: readonly string[], limit: number): string {
|
|
11
7
|
if (names.length <= limit) return names.join(", ");
|
|
12
8
|
return `${names.slice(0, limit).join(", ")} … and ${names.length - limit} more`;
|
|
@@ -22,23 +18,15 @@ export interface EngineLifecycleDeps<E extends RevivableEngine> {
|
|
|
22
18
|
create(): E;
|
|
23
19
|
/** Tears the current engine down, flushing its final snapshot. */
|
|
24
20
|
dispose(engine: E): Promise<void>;
|
|
25
|
-
/**
|
|
26
|
-
* Tears down an engine that cannot cooperate — a wedged guest cannot serve
|
|
27
|
-
* the snapshot flush dispose would ask of it. Falls back to dispose.
|
|
28
|
-
*/
|
|
21
|
+
/** Kill-then-rebuild when a wedged engine cannot serve the snapshot flush. */
|
|
29
22
|
discard?(engine: E): Promise<void>;
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
/**
|
|
33
|
-
* Why an engine came into existence. `startup` is the expected path and is
|
|
34
|
-
* already announced in the transcript; `cell` means an engine had to be built
|
|
35
|
-
* to serve a tool call, which only happens when the previous one went away
|
|
36
|
-
* mid-session — the case the model needs told about in-band.
|
|
37
|
-
*/
|
|
25
|
+
/** `startup` is announced in the transcript; `cell` means an engine was rebuilt mid-session and needs an in-band notice. */
|
|
38
26
|
export type AcquireOrigin = "startup" | "cell";
|
|
39
27
|
|
|
40
28
|
function formatEngineResetNotice(restore: RestoreResult | null): string {
|
|
41
|
-
const lines = ["<
|
|
29
|
+
const lines = ["<repl_engine_reset>"];
|
|
42
30
|
if (!restore) {
|
|
43
31
|
// --- no snapshot at all: namespace is genuinely empty ---
|
|
44
32
|
lines.push(
|
|
@@ -73,7 +61,7 @@ function formatEngineResetNotice(restore: RestoreResult | null): string {
|
|
|
73
61
|
}
|
|
74
62
|
lines.push("Anything defined after the last snapshot is also gone.");
|
|
75
63
|
}
|
|
76
|
-
lines.push("Re-verify a variable before reusing it, especially inside shell interpolation.", "</
|
|
64
|
+
lines.push("Re-verify a variable before reusing it, especially inside shell interpolation.", "</repl_engine_reset>");
|
|
77
65
|
return lines.join("\n");
|
|
78
66
|
}
|
|
79
67
|
|
|
@@ -81,25 +69,19 @@ export class EngineLifecycle<E extends RevivableEngine> {
|
|
|
81
69
|
private engine?: E;
|
|
82
70
|
private revival?: Promise<RestoreResult | null>;
|
|
83
71
|
private pendingNotice?: string;
|
|
84
|
-
/** Teardown in progress; a rebuild must not overlap the final snapshot flush. */
|
|
85
72
|
private teardown?: Promise<void>;
|
|
86
|
-
/** First-build in progress
|
|
73
|
+
/** First-build in progress. */
|
|
87
74
|
private acquiring?: Promise<{ engine: E; restore: RestoreResult | null; created: boolean }>;
|
|
88
75
|
|
|
89
76
|
constructor(private readonly deps: EngineLifecycleDeps<E>) {}
|
|
90
77
|
|
|
91
|
-
/**
|
|
92
|
-
* The live engine, built and revived if it does not exist yet.
|
|
93
|
-
* Revival is awaited here so a caller never sees an un-revived namespace.
|
|
94
|
-
*/
|
|
78
|
+
/** Built and revived on demand; awaited so callers never see an un-revived namespace. */
|
|
95
79
|
async acquire(origin: AcquireOrigin): Promise<{ engine: E; restore: RestoreResult | null; created: boolean }> {
|
|
96
80
|
if (this.engine) {
|
|
97
81
|
return { engine: this.engine, restore: await this.revival!, created: false };
|
|
98
82
|
}
|
|
99
|
-
// --- two concurrent acquires on an empty engine must share one build ---
|
|
100
83
|
if (this.acquiring) return this.acquiring;
|
|
101
84
|
const build = (async () => {
|
|
102
|
-
// --- a teardown flushing its final snapshot must finish before the rebuild reads it ---
|
|
103
85
|
while (this.teardown) await this.teardown;
|
|
104
86
|
if (this.engine) {
|
|
105
87
|
const held: E = this.engine;
|
|
@@ -131,11 +113,7 @@ export class EngineLifecycle<E extends RevivableEngine> {
|
|
|
131
113
|
await this.teardownWith((engine) => this.deps.dispose(engine));
|
|
132
114
|
}
|
|
133
115
|
|
|
134
|
-
/**
|
|
135
|
-
* Teardown for an engine that cannot cooperate (e.g. wedged in synchronous
|
|
136
|
-
* code). Skips the snapshot flush a graceful dispose would attempt; the next
|
|
137
|
-
* acquire builds a fresh engine revived from the last completed snapshot.
|
|
138
|
-
*/
|
|
116
|
+
/** Kill-then-rebuild for a wedged engine; skips the final snapshot flush, uses the last good one. */
|
|
139
117
|
async discard(): Promise<void> {
|
|
140
118
|
await this.teardownWith((engine) => (this.deps.discard ?? this.deps.dispose)(engine));
|
|
141
119
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// --- tool-meta: thin surface assembling the execute tool's prompt from pure modules ---
|
|
2
|
-
// --- the model contract lives in prompt.ts; only the
|
|
2
|
+
// --- the model contract lives in prompt.ts; only the helpers wiring stays here ---
|
|
3
3
|
|
|
4
|
+
import { buildHelpersMap } from "./helpers.js";
|
|
4
5
|
import { buildPromptGuidelines, executePromptSnippet, executeToolDescription } from "./prompt.js";
|
|
5
|
-
import { buildToolboxMap } from "./toolbox.js";
|
|
6
6
|
|
|
7
7
|
export const EXECUTE_DESCRIPTION = executeToolDescription;
|
|
8
8
|
export const EXECUTE_PROMPT_SNIPPET = executePromptSnippet;
|
|
9
9
|
|
|
10
|
-
// --- build the guidelines from the
|
|
11
|
-
export function buildExecutePromptGuidelines(
|
|
12
|
-
const map =
|
|
13
|
-
const preloaded = map.length > 0 ? map : [
|
|
10
|
+
// --- build the guidelines from the one helpers dir (default ~/.pi/agent/pi-repl/helpers) ---
|
|
11
|
+
export function buildExecutePromptGuidelines(): string[] {
|
|
12
|
+
const map = buildHelpersMap();
|
|
13
|
+
const preloaded = map.length > 0 ? map : [];
|
|
14
14
|
return buildPromptGuidelines(preloaded);
|
|
15
15
|
}
|
package/docs/how-to-functions.md
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# How to add a toolbox function
|
|
2
|
-
|
|
3
|
-
A toolbox function is one `.py` file that pi-repl loads into every kernel and
|
|
4
|
-
surfaces to the model through the `execute` tool's prompt guidance (its
|
|
5
|
-
signature + one-line summary appears in `promptGuidelines`). Add a file, and it
|
|
6
|
-
shows up wherever the toolbox is read.
|
|
7
|
-
|
|
8
|
-
> **When a change shows up.** The kernel loads the toolbox at boot, and the
|
|
9
|
-
> `execute` tool builds its function list at registration (module load), so a
|
|
10
|
-
> toolbox change (add/remove a file, rename one with a `_` prefix) is picked up
|
|
11
|
-
> by a **session restart / `/reload`** — not mid-session.
|
|
12
|
-
|
|
13
|
-
## Where functions live
|
|
14
|
-
|
|
15
|
-
By default the extension ships four (`read`, `write`, `edit`, `bash`) in
|
|
16
|
-
`src/engine/toolbox/`. To use your **own** set, set `toolboxDir` in your
|
|
17
|
-
config:
|
|
18
|
-
|
|
19
|
-
```jsonc
|
|
20
|
-
// ~/.pi/agent/pi-repl/config.json
|
|
21
|
-
{ "toolboxDir": "~/.pi/agent/pi-repl/functions" }
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Use an absolute path or a `~`-prefixed one (`~` expands to your home). A bare relative
|
|
25
|
-
path resolves from the process working directory, which is not reliable, so prefer
|
|
26
|
-
an absolute path for a stable per-user folder. Point `toolboxDir` at a directory and
|
|
27
|
-
every `*.py` there is loaded **in addition to** the shipped `read`/`write`/`edit`/`bash`.
|
|
28
|
-
If a file in your folder has the **same name** as a built-in (e.g. `read.py`), your
|
|
29
|
-
version wins and the built-in is ignored for that name.
|
|
30
|
-
|
|
31
|
-
## The file contract
|
|
32
|
-
|
|
33
|
-
Every toolbox file must:
|
|
34
|
-
|
|
35
|
-
1. have a `def` whose signature is the real call an agent would use, and
|
|
36
|
-
2. may declare `function_description` (a short one-line summary shown in the
|
|
37
|
-
prompt).
|
|
38
|
-
|
|
39
|
-
A minimal, valid file:
|
|
40
|
-
|
|
41
|
-
```python
|
|
42
|
-
# pi-repl/functions/summarize.py
|
|
43
|
-
function_description = """Return a first-sentence summary of a text."""
|
|
44
|
-
|
|
45
|
-
__all__ = ["summarize"]
|
|
46
|
-
|
|
47
|
-
def summarize(text, limit=1):
|
|
48
|
-
return ". ".join(text.split(". ")[:limit]) + "."
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
That is everything. `summarize` loads into the kernel and the `execute` tool's
|
|
52
|
-
prompt guidance shows `summarize(text, limit=1)` after the next session restart.
|
|
53
|
-
|
|
54
|
-
## The two pieces the loader reads
|
|
55
|
-
|
|
56
|
-
**1. The signature comes from the `def`, not the description.**
|
|
57
|
-
Arguments are read from the actual `def` line rather than hand-copied into a
|
|
58
|
-
docstring, so the signature the model sees tracks the code for a normal
|
|
59
|
-
single-line signature. Change `def summarize(text,
|
|
60
|
-
limit=1):` to `limit=200`, and after the next session restart the prompt
|
|
61
|
-
updates to match.
|
|
62
|
-
|
|
63
|
-
**2. The description, from `function_description`, optional.**
|
|
64
|
-
Used as the one-line summary in the `execute` tool's prompt guidance. If you
|
|
65
|
-
omit it, the function is still advertised (by signature), just without a
|
|
66
|
-
one-liner.
|
|
67
|
-
|
|
68
|
-
Each file should also give the function a real docstring (the text under
|
|
69
|
-
`def`). That docstring is shown by `help(name)` in the kernel and carries the
|
|
70
|
-
deeper usage and gotchas. It does not go into the execute tool's prompt
|
|
71
|
-
guidance (only the one-line `function_description` does). Keep it for
|
|
72
|
-
details, the venv note, and edge cases.
|
|
73
|
-
|
|
74
|
-
## How much to document
|
|
75
|
-
|
|
76
|
-
`function_description` is the summary; the `def` docstring is the detail. A
|
|
77
|
-
good `function_description` is one line ("Run a shell command and return its
|
|
78
|
-
result."). A good docstring explains arguments, return value, and any
|
|
79
|
-
non-obvious behavior, including environment facts the model needs
|
|
80
|
-
("the evaluator runs in a project-local venv, not the system python").
|
|
81
|
-
|
|
82
|
-
## Disabling a file without deleting it
|
|
83
|
-
|
|
84
|
-
Rename the file to start with an underscore: `_test_helper.py`. The loader
|
|
85
|
-
**(and the execute tool's prompt guidance)** skip underscore-prefixed files, so
|
|
86
|
-
it never reaches the kernel or the model. Use this for scratch or internal
|
|
87
|
-
helpers.
|
|
88
|
-
|
|
89
|
-
## Good practice
|
|
90
|
-
|
|
91
|
-
- One function per file, name matches the function.
|
|
92
|
-
- Keep `function_description` one line. Everything else goes in the docstring.
|
|
93
|
-
- Let the signature carry the truth; the description says what it's *for*.
|
|
94
|
-
- A function that can hang (a shell call, network) should say so in its
|
|
95
|
-
docstring so the model knows the trade-off.
|
|
96
|
-
|
|
97
|
-
## Confirming it worked
|
|
98
|
-
|
|
99
|
-
At a `pi --repl` prompt, run a cell:
|
|
100
|
-
|
|
101
|
-
```python
|
|
102
|
-
print(ls()) # list what's loaded
|
|
103
|
-
print(help('summarize')) # signature + full docstring details
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
If `summarize` shows up in `ls()` and `help`, it loaded. The `execute` tool's
|
|
107
|
-
prompt guidance also lists it (same first-line summary) after the next session
|
|
108
|
-
restart.
|