pi-brain 0.1.4 → 0.1.6

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 CHANGED
@@ -6,6 +6,8 @@
6
6
 
7
7
  Versioned memory for the [pi coding agent](https://github.com/badlogic/pi-mono). Agents commit decisions and reasoning to a `.memory/` directory, preserving context across sessions, compactions, and model switches.
8
8
 
9
+ Credit: This project is my implementation for Pi of this paper [Git Context Controller: Manage the Context of LLM-based Agents like Git](https://arxiv.org/html/2508.00031v1)
10
+
9
11
  ## Getting Started
10
12
 
11
13
  ```bash
@@ -14,7 +16,7 @@ pi install npm:pi-brain
14
16
 
15
17
  Open pi in any project and say "initialize Brain" (or run `/skill:brain`). The agent creates `.memory/` and starts remembering.
16
18
 
17
- That's it. The agent decides when to commit, branch, and merge — you don't need to manage anything.
19
+ That's it. The agent decides when to commit, branch, and merge — you don't **need** to manage anything. However, you can always prompt the agent to remember something specific if you'd like.
18
20
 
19
21
  ## How It Works
20
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-brain",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Versioned memory extension for the pi coding agent",
5
5
  "keywords": [
6
6
  "agent-memory",
@@ -76,6 +76,28 @@ For deep retrieval, use `read` directly:
76
76
  A subagent handles commit distillation — it reads your `log.md` and prior commits,
77
77
  then produces the structured commit entry. You just provide a good `summary` string.
78
78
 
79
+ ## After Every Commit
80
+
81
+ **Re-read `.memory/main.md` and rewrite stale sections.** The roadmap is the first
82
+ thing a new session reads — if it's stale, every future session starts with a wrong
83
+ picture. This means genuine review, not mechanical edits.
84
+
85
+ After `memory_commit` returns:
86
+
87
+ 1. Read `.memory/main.md` in full.
88
+ 2. Rewrite the **Current State** section to describe what is true _right now_ — remove
89
+ historical context that has migrated to Key Decisions or Milestones.
90
+ 3. Add any new entries to **Key Decisions Made**.
91
+ 4. Move completed items in **Milestones** and add new planned ones.
92
+ 5. Remove or rewrite anything a new reader would find misleading.
93
+
94
+ The goal is curation, not accumulation. A reader of Current State should understand
95
+ the project as it exists today without wading through history.
96
+
97
+ For trivial commits that don't change the project's state, decisions, or milestones
98
+ (e.g., minor refactors, typo fixes), you can pass `update_roadmap: false` to skip
99
+ the reminder. Most commits should update the roadmap.
100
+
79
101
  ## When to Branch
80
102
 
81
103
  - You want to explore an alternative approach without contaminating current thinking
@@ -40,6 +40,17 @@ Each commit in `commits.md` has three blocks:
40
40
 
41
41
  The latest commit always contains a self-contained summary of the full branch history.
42
42
 
43
+ ## When to Commit
44
+
45
+ Call `memory_commit` when one of these is true:
46
+
47
+ - You reached a stable decision or understanding worth preserving.
48
+ - You finished an exploration branch with a clear conclusion.
49
+ - You are about to change direction significantly.
50
+ - You completed meaningful progress and are about to end the session.
51
+ - The extension warns that `log.md` is getting large.
52
+ - You are about to claim the task is complete or hand off to another agent.
53
+
43
54
  ## Conventions
44
55
 
45
56
  - **Agent-driven**: You decide when to commit, branch, and merge
@@ -47,3 +58,4 @@ The latest commit always contains a self-contained summary of the full branch hi
47
58
  - **Rolling summaries**: Each commit re-synthesizes all prior progress
48
59
  - **No direct log.md writes**: The extension maintains log.md automatically
49
60
  - **Status is automatic**: Memory status is injected at session start and appended to tool results (compact/truncated when large; use `read .memory/main.md` for full roadmap)
61
+ - **Keep main.md current**: After every commit, re-read `.memory/main.md` in full and rewrite stale sections. Current State should describe what is true _right now_ — remove historical context that belongs in Key Decisions or Milestones. The goal is curation, not accumulation. For trivial commits that don't change project state, pass `update_roadmap: false` to skip the reminder.
package/src/index.ts CHANGED
@@ -101,7 +101,7 @@ function resolveSkillPath(): string {
101
101
  export default function activate(pi: ExtensionAPI) {
102
102
  let state: MemoryState | null = null;
103
103
  let branchManager: BranchManager | null = null;
104
- let statusInjected = false;
104
+ let frozenStatusSnapshot: string | null = null;
105
105
 
106
106
  function tryLoad(ctx: ExtensionContext): boolean {
107
107
  if (isMemoryReady(state, branchManager)) {
@@ -127,12 +127,10 @@ export default function activate(pi: ExtensionAPI) {
127
127
  description:
128
128
  "Manage memory branches. Actions: create (new branch), switch (change active branch), merge (synthesize branch into current).",
129
129
  parameters: Type.Object({
130
- action: Type.Union(
131
- [Type.Literal("create"), Type.Literal("switch"), Type.Literal("merge")],
132
- {
133
- description: 'Action to perform: "create", "switch", or "merge"',
134
- }
135
- ),
130
+ action: Type.String({
131
+ enum: ["create", "switch", "merge"],
132
+ description: 'Action to perform: "create", "switch", or "merge"',
133
+ }),
136
134
  name: Type.Optional(
137
135
  Type.String({ description: "Branch name (required for create)" })
138
136
  ),
@@ -180,7 +178,12 @@ export default function activate(pi: ExtensionAPI) {
180
178
  description: "Checkpoint a milestone in agent memory.",
181
179
  parameters: Type.Object({
182
180
  summary: Type.String({ description: "Short summary of this checkpoint" }),
183
- update_roadmap: Type.Optional(Type.Boolean()),
181
+ update_roadmap: Type.Optional(
182
+ Type.Boolean({
183
+ description:
184
+ "Update .memory/main.md after commit. Defaults to true — set false to skip for trivial commits.",
185
+ })
186
+ ),
184
187
  }),
185
188
  async execute(_toolCallId, params, signal, _onUpdate, ctx) {
186
189
  if (
@@ -214,7 +217,8 @@ export default function activate(pi: ExtensionAPI) {
214
217
  commitContent,
215
218
  state,
216
219
  branchManager,
217
- ctx.cwd
220
+ ctx.cwd,
221
+ params.update_roadmap
218
222
  );
219
223
 
220
224
  setBrainFooterStatus(ctx, state, branchManager);
@@ -226,7 +230,7 @@ export default function activate(pi: ExtensionAPI) {
226
230
  state = new MemoryState(ctx.cwd);
227
231
  state.load();
228
232
  branchManager = new BranchManager(ctx.cwd);
229
- statusInjected = false;
233
+ frozenStatusSnapshot = null;
230
234
 
231
235
  if (!state.isInitialized) {
232
236
  setBrainFooterStatus(ctx, state, branchManager);
@@ -249,7 +253,7 @@ export default function activate(pi: ExtensionAPI) {
249
253
  });
250
254
 
251
255
  pi.on("session_switch", (_event, ctx) => {
252
- statusInjected = false;
256
+ frozenStatusSnapshot = null;
253
257
 
254
258
  state = new MemoryState(ctx.cwd);
255
259
  state.load();
@@ -262,11 +266,7 @@ export default function activate(pi: ExtensionAPI) {
262
266
  setBrainFooterStatus(ctx, state, branchManager);
263
267
  });
264
268
 
265
- pi.on("before_agent_start", (_event: BeforeAgentStartEvent, ctx) => {
266
- if (statusInjected) {
267
- return;
268
- }
269
-
269
+ pi.on("before_agent_start", (event: BeforeAgentStartEvent, ctx) => {
270
270
  if (
271
271
  !tryLoad(ctx) ||
272
272
  !isMemoryReady(state, branchManager) ||
@@ -275,25 +275,21 @@ export default function activate(pi: ExtensionAPI) {
275
275
  return;
276
276
  }
277
277
 
278
- statusInjected = true;
278
+ if (frozenStatusSnapshot === null) {
279
+ frozenStatusSnapshot = buildStatusView(state, branchManager, ctx.cwd, {
280
+ compact: true,
281
+ roadmapCharLimit: BEFORE_AGENT_START_ROADMAP_CHAR_LIMIT,
282
+ branchLimit: BEFORE_AGENT_START_BRANCH_LIMIT,
283
+ });
284
+ }
279
285
 
280
- const status = buildStatusView(state, branchManager, ctx.cwd, {
281
- compact: true,
282
- roadmapCharLimit: BEFORE_AGENT_START_ROADMAP_CHAR_LIMIT,
283
- branchLimit: BEFORE_AGENT_START_BRANCH_LIMIT,
284
- });
285
286
  return {
286
- message: {
287
- customType: "brain-status",
288
- content: status,
289
- display: true,
290
- details: {},
291
- },
287
+ systemPrompt: `${event.systemPrompt}\n\n${frozenStatusSnapshot}`,
292
288
  };
293
289
  });
294
290
 
295
291
  pi.on("session_compact", () => {
296
- statusInjected = false;
292
+ frozenStatusSnapshot = null;
297
293
  });
298
294
 
299
295
  pi.on("resources_discover", () => ({
@@ -34,7 +34,8 @@ export function finalizeMemoryCommit(
34
34
  commitContent: string,
35
35
  state: MemoryState,
36
36
  branches: BranchManager,
37
- projectDir: string
37
+ projectDir: string,
38
+ updateRoadmap?: boolean
38
39
  ): string {
39
40
  const branch = state.activeBranch;
40
41
  const hash = generateHash();
@@ -60,5 +61,11 @@ export function finalizeMemoryCommit(
60
61
  const status = buildStatusView(state, branches, projectDir, {
61
62
  compact: true,
62
63
  });
63
- return `${resultText}\n\n${status}`;
64
+
65
+ const roadmapReminder =
66
+ updateRoadmap === false
67
+ ? ""
68
+ : "\n\n**Action required:** Re-read `.memory/main.md` in full and rewrite stale sections. Current State should describe what is true right now — curate, don't just append.";
69
+
70
+ return `${resultText}\n\n${status}${roadmapReminder}`;
64
71
  }