pi-repl-py 0.1.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/ARCHITECTURE.md +141 -0
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/docs/how-to-functions.md +107 -0
- package/docs/philosophy.md +88 -0
- package/index.ts +220 -0
- package/package.json +57 -0
- package/scripts/setup-venv.mjs +71 -0
- package/src/engine/guest.py +317 -0
- package/src/engine/index.ts +656 -0
- package/src/engine/protocol.ts +66 -0
- package/src/engine/toolbox/bash.py +72 -0
- package/src/engine/toolbox/edit.py +37 -0
- package/src/engine/toolbox/read.py +26 -0
- package/src/engine/toolbox/write.py +23 -0
- package/src/extension/config.ts +65 -0
- package/src/extension/preview-core.ts +518 -0
- package/src/extension/render-core.ts +348 -0
- package/src/extension/render.ts +93 -0
- package/src/extension/session-engine.ts +155 -0
- package/src/extension/tool-meta.ts +58 -0
- package/src/extension/toolbox.ts +74 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads the toolbox functions from the toolbox directory.
|
|
3
|
+
*
|
|
4
|
+
* The prompt-facing function map is derived from the real source, never
|
|
5
|
+
* hard-coded: each function's `def <name>(<args>)` line supplies the signature
|
|
6
|
+
* (authoritative) and the `function_description = """..."""` docstring supplies
|
|
7
|
+
* the one-line "what it does". Same contract the guest's `ls()`/`help()` uses,
|
|
8
|
+
* so what the prompt advertises always matches what the kernel loaded.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
|
|
14
|
+
interface ToolEntry {
|
|
15
|
+
name: string;
|
|
16
|
+
call: string; // e.g. "read(path, offset=1, limit=None)"
|
|
17
|
+
description: string; // first line of function_description, "" if absent
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Read a `function_description = """..."""` value; keep its first line. */
|
|
21
|
+
function parseDescription(source: string): string {
|
|
22
|
+
const m = source.match(/function_description\s*=\s*"""\s*([^\n]*)/);
|
|
23
|
+
if (!m) return "";
|
|
24
|
+
return m[1].replace(/"""\s*$/, "").trim();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Regex the call signature from `def name(args):`. */
|
|
28
|
+
function parseDefCall(source: string): string | null {
|
|
29
|
+
const m = source.match(/def\s+([A-Za-z_]\w*)\s*\(([^)]*)\)/);
|
|
30
|
+
if (!m) return null;
|
|
31
|
+
const name = m[1];
|
|
32
|
+
const args = m[2].trim();
|
|
33
|
+
return args ? `${name}(${args})` : `${name}()`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Default toolbox directory: the shipped src/engine/toolbox. Resolved at
|
|
38
|
+
* runtime via the module path so it stays correct after packaging.
|
|
39
|
+
*/
|
|
40
|
+
function defaultToolboxDir(): string {
|
|
41
|
+
return join(import.meta.dirname, "..", "..", "src", "engine", "toolbox");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Load {function_name → source} for each non-underscore *.py in `dir`. */
|
|
45
|
+
function loadToolboxEntries(dir: string | undefined): ToolEntry[] {
|
|
46
|
+
const d = dir && dir.length > 0 ? dir : defaultToolboxDir();
|
|
47
|
+
if (!existsSync(d)) return [];
|
|
48
|
+
const entries: ToolEntry[] = [];
|
|
49
|
+
for (const file of readdirSync(d).sort()) {
|
|
50
|
+
if (!file.endsWith(".py")) continue;
|
|
51
|
+
const name = file.slice(0, -3);
|
|
52
|
+
if (!/^[A-Za-z_]\w*$/.test(name)) continue;
|
|
53
|
+
// An underscore-prefixed file is not loaded into the kernel (see guest.py),
|
|
54
|
+
// so it must never be advertised either.
|
|
55
|
+
if (name.startsWith("_")) continue;
|
|
56
|
+
try {
|
|
57
|
+
const source = readFileSync(join(d, file), "utf8");
|
|
58
|
+
const call = parseDefCall(source);
|
|
59
|
+
if (!call) continue;
|
|
60
|
+
entries.push({ name, call, description: parseDescription(source) });
|
|
61
|
+
} catch {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return entries;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The markdown-style function map: one `- call: description` line per function,
|
|
70
|
+
* ready to drop into the prompt guidelines.
|
|
71
|
+
*/
|
|
72
|
+
export function buildToolboxMap(dir: string | undefined): string[] {
|
|
73
|
+
return loadToolboxEntries(dir).map((t) => (t.description ? `- ${t.call}: ${t.description}` : `- ${t.call}`));
|
|
74
|
+
}
|