pi-soul 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Akshay Patel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # pi-soul
2
+
3
+ Give your [pi](https://pi.dev) agent a **soul** — a persistent persona/profile that tailors every session's output.
4
+
5
+ Write your soul once in markdown, and pi-soul injects it into the system prompt of every session, in every project. Your agent stops being generic and starts being *yours*.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ # from npm
11
+ pi install npm:pi-soul
12
+
13
+ # or straight from git
14
+ pi install git:github.com/Akshay-patel7/pi-soul
15
+ ```
16
+
17
+ Try it without installing:
18
+
19
+ ```bash
20
+ pi -e npm:pi-soul
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ | Command | What it does |
26
+ | ------------ | ------------------------------------------------------- |
27
+ | `/soul` | Show the active soul (source, path, preview) and state |
28
+ | `/soul edit` | Create or edit your soul in an editor |
29
+ | `/soul on` | Enable soul injection for this session (default) |
30
+ | `/soul off` | Disable soul injection for this session |
31
+
32
+ When a soul is active you'll see `soul: global` (or `soul: project`) in the footer.
33
+
34
+ ## Where your soul lives
35
+
36
+ pi-soul resolves the soul fresh on every turn, so edits apply immediately:
37
+
38
+ 1. **Project soul** — `<project>/.pi/soul.md` (only in trusted projects). Wins when present. Great for team-shared personas checked into a repo.
39
+ 2. **Global soul** — `~/.pi/agent/soul.md`. Your default persona everywhere.
40
+
41
+ The soul file is plain markdown — edit it with `/soul edit` or any editor.
42
+
43
+ ## Example soul
44
+
45
+ ```markdown
46
+ # My Soul
47
+
48
+ You are an elite software engineer — the greatest the world has ever seen.
49
+ Incredibly smart and deeply knowledgeable, you ground every claim in facts,
50
+ not speculation. You are an outstanding problem solver who cuts straight to
51
+ the root cause, and you write clean, simple, maintainable code. You are one
52
+ of those rare engineers that are hard to come by.
53
+ ```
54
+
55
+ With this soul, every new pi session reasons, communicates, and codes like that engineer — no per-session prompting required.
56
+
57
+ ## How it works
58
+
59
+ A single TypeScript extension listens for pi's `before_agent_start` event and appends your soul to the system prompt under a `# Your Soul` section. No build step, no runtime dependencies — pi loads the TypeScript source directly.
60
+
61
+ ## License
62
+
63
+ MIT
@@ -0,0 +1,163 @@
1
+ /**
2
+ * pi-soul — give your pi agent a soul.
3
+ *
4
+ * A "soul" is a persistent persona/profile written in markdown. When present,
5
+ * it is appended to the system prompt on every turn so the agent's output is
6
+ * tailored to it in every session.
7
+ *
8
+ * Soul resolution (checked fresh on each agent start):
9
+ * 1. Project soul: <cwd>/.pi/soul.md (only when the project is trusted)
10
+ * 2. Global soul: ~/.pi/agent/soul.md
11
+ *
12
+ * Commands:
13
+ * /soul show the active soul (source, path, preview) and state
14
+ * /soul edit create or edit the soul in an editor
15
+ * /soul on enable soul injection for this session (default)
16
+ * /soul off disable soul injection for this session
17
+ */
18
+
19
+ import {
20
+ CONFIG_DIR_NAME,
21
+ getAgentDir,
22
+ type ExtensionAPI,
23
+ type ExtensionContext,
24
+ } from "@earendil-works/pi-coding-agent";
25
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
26
+ import { dirname, join } from "node:path";
27
+
28
+ const STARTER_SOUL = `# My Soul
29
+
30
+ You are an elite software engineer — the greatest the world has ever seen.
31
+ Incredibly smart and deeply knowledgeable, you ground every claim in facts,
32
+ not speculation. You are an outstanding problem solver who cuts straight to
33
+ the root cause, and you write clean, simple, maintainable code. You are one
34
+ of those rare engineers that are hard to come by.
35
+
36
+ <!-- Edit this file to shape your agent's persona. Delete anything you like. -->
37
+ `;
38
+
39
+ interface ResolvedSoul {
40
+ source: "project" | "global";
41
+ path: string;
42
+ content: string;
43
+ }
44
+
45
+ function globalSoulPath(): string {
46
+ return join(getAgentDir(), "soul.md");
47
+ }
48
+
49
+ function projectSoulPath(cwd: string): string {
50
+ return join(cwd, CONFIG_DIR_NAME, "soul.md");
51
+ }
52
+
53
+ function readSoulFile(path: string): string | undefined {
54
+ try {
55
+ if (!existsSync(path)) return undefined;
56
+ const content = readFileSync(path, "utf8").trim();
57
+ return content.length > 0 ? content : undefined;
58
+ } catch {
59
+ return undefined;
60
+ }
61
+ }
62
+
63
+ /** Resolve the active soul: project (trusted only) wins over global. */
64
+ function resolveSoul(ctx: ExtensionContext): ResolvedSoul | undefined {
65
+ if (ctx.isProjectTrusted()) {
66
+ const path = projectSoulPath(ctx.cwd);
67
+ const content = readSoulFile(path);
68
+ if (content) return { source: "project", path, content };
69
+ }
70
+ const path = globalSoulPath();
71
+ const content = readSoulFile(path);
72
+ if (content) return { source: "global", path, content };
73
+ return undefined;
74
+ }
75
+
76
+ export default function soulExtension(pi: ExtensionAPI) {
77
+ let enabled = true;
78
+
79
+ const updateStatus = (ctx: ExtensionContext) => {
80
+ if (!ctx.hasUI) return;
81
+ const soul = enabled ? resolveSoul(ctx) : undefined;
82
+ ctx.ui.setStatus("soul", soul ? `soul: ${soul.source}` : undefined);
83
+ };
84
+
85
+ pi.on("session_start", async (_event, ctx) => {
86
+ updateStatus(ctx);
87
+ });
88
+
89
+ // Inject the soul into the system prompt on every turn.
90
+ pi.on("before_agent_start", async (event, ctx) => {
91
+ if (!enabled) return undefined;
92
+ const soul = resolveSoul(ctx);
93
+ if (!soul) return undefined;
94
+ return {
95
+ systemPrompt: `${event.systemPrompt}
96
+
97
+ # Your Soul
98
+
99
+ The user has given you the following soul — a persona and profile you must
100
+ embody. Let it shape your reasoning, tone, and output in every response,
101
+ while still completing every task correctly and accurately.
102
+
103
+ ${soul.content}`,
104
+ };
105
+ });
106
+
107
+ pi.registerCommand("soul", {
108
+ description: "Manage your agent's soul (persona). Usage: /soul [edit|on|off]",
109
+ handler: async (args, ctx) => {
110
+ const sub = (args ?? "").trim().toLowerCase();
111
+
112
+ if (sub === "on" || sub === "off") {
113
+ enabled = sub === "on";
114
+ updateStatus(ctx);
115
+ ctx.ui.notify(enabled ? "Soul enabled" : "Soul disabled for this session", "info");
116
+ return;
117
+ }
118
+
119
+ if (sub === "edit") {
120
+ if (!ctx.hasUI) {
121
+ ctx.ui.notify(`No interactive UI. Edit your soul directly: ${globalSoulPath()}`, "warning");
122
+ return;
123
+ }
124
+ // Edit the active soul file; fall back to creating the global one.
125
+ const active = resolveSoul(ctx);
126
+ const path = active?.path ?? globalSoulPath();
127
+ const prefill = active?.content ?? STARTER_SOUL;
128
+ const result = await ctx.ui.editor(`Edit soul (${path})`, prefill);
129
+ if (result === undefined) {
130
+ ctx.ui.notify("Soul edit cancelled", "info");
131
+ return;
132
+ }
133
+ mkdirSync(dirname(path), { recursive: true });
134
+ writeFileSync(path, result, "utf8");
135
+ updateStatus(ctx);
136
+ ctx.ui.notify(
137
+ result.trim().length > 0 ? `Soul saved to ${path}` : `Soul cleared (${path} is empty)`,
138
+ "info",
139
+ );
140
+ return;
141
+ }
142
+
143
+ if (sub === "") {
144
+ const soul = resolveSoul(ctx);
145
+ if (!soul) {
146
+ ctx.ui.notify(
147
+ `No soul found. Run /soul edit to create one (global: ${globalSoulPath()})`,
148
+ "info",
149
+ );
150
+ return;
151
+ }
152
+ const preview = soul.content.split("\n").find((l) => l.trim() && !l.startsWith("#")) ?? "";
153
+ ctx.ui.notify(
154
+ `Soul: ${soul.source} (${soul.path}) — ${enabled ? "enabled" : "disabled"}\n${preview}`,
155
+ "info",
156
+ );
157
+ return;
158
+ }
159
+
160
+ ctx.ui.notify("Usage: /soul [edit|on|off]", "warning");
161
+ },
162
+ });
163
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "pi-soul",
3
+ "version": "0.1.0",
4
+ "description": "Give your pi agent a soul — a persistent persona that tailors every session.",
5
+ "keywords": [
6
+ "pi-package",
7
+ "pi",
8
+ "persona",
9
+ "profile",
10
+ "soul"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "Akshay Patel",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/Akshay-patel7/pi-soul.git"
17
+ },
18
+ "files": [
19
+ "extensions",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "scripts": {
24
+ "typecheck": "tsc --noEmit"
25
+ },
26
+ "pi": {
27
+ "extensions": [
28
+ "./extensions"
29
+ ]
30
+ },
31
+ "peerDependencies": {
32
+ "@earendil-works/pi-coding-agent": "*"
33
+ },
34
+ "devDependencies": {
35
+ "@earendil-works/pi-coding-agent": "latest",
36
+ "typescript": "^5"
37
+ }
38
+ }