Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions integrations/pi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ For normal installs, leave `bmPath` alone and make sure `bm` is on Pi's PATH. Wo

## Commands and tools

- `/bm-status` — show effective package settings.
- `/bm-status` — show the explicit project (or `unconfigured`), workspace trust, and effective automation state. Auto recall/capture report `blocked` with the reason when a project mapping or workspace trust is missing, and `off (configured)` when disabled in settings.
- `/bm-recall [topic]` — search recent Pi checkpoints and inject fenced reference data.
- `/bm-capture [title]` — write the current working thread as a `pi_session` note.
- `bm_recall` — LLM-callable recall tool.
Expand Down Expand Up @@ -136,7 +136,7 @@ Model-backed end-to-end runs should use temporary `BASIC_MEMORY_HOME`, `BASIC_ME

## Privacy defaults

Automatic recall and capture are enabled by default so a configured project gets continuity immediately. With no explicit `.pi/basic-memory.json` project mapping, recall shows setup guidance and hook-backed capture has no write destination, so it does not silently write to an ambient default project.
Automatic recall and capture default to enabled in configuration, but run only with an explicit project mapping and workspace trust. With no explicit `.pi/basic-memory.json` project mapping, manual recall shows setup guidance and automatic recall/capture are blocked, so they do not silently use an ambient default project.

The package uses the shared `bm hook --harness pi` flow by default so Pi follows the same predictable Basic Memory lifecycle contract as other agent harnesses. Set `BASIC_MEMORY_PI_TRUST_WORKSPACE=1` only for workspaces you trust to enable automatic recall/capture from that workspace's project mapping. Set `"autoRecall": false`, `"autoCapture": false`, or `"useHookFlow": false` to make the behavior quieter.

Expand Down
34 changes: 24 additions & 10 deletions integrations/pi/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,29 @@ function formatError(error: unknown): string {
return error instanceof Error ? error.message : String(error);
}

export function formatStatus(cfg: BasicMemoryPiConfig, workspaceTrusted: boolean): string {
const configured = Boolean(cfg.project || cfg.projectId);
const blocked = !configured ? "no project mapping" : !workspaceTrusted ? "workspace not trusted" : undefined;
const automation = (enabled: boolean): string => !enabled
? "off (configured)"
: blocked ? `blocked (${blocked})` : "on";
const lines = [
`transport: ${cfg.transport}`,
`bm: ${(cfg.bmCommand ?? [cfg.bmPath]).join(" ")}`,
`project: ${cfg.projectId ? `id:${cfg.projectId}` : cfg.project ?? "unconfigured"}`,
`workspace trust: ${workspaceTrusted ? "on" : "off"}`,
`capture folder: ${cfg.captureFolder}`,
`auto recall: ${automation(cfg.autoRecall)}`,
`auto capture: ${automation(cfg.autoCapture)}`,
`hook flow: ${cfg.useHookFlow ? "on" : "off"}`,
];
if (!configured) lines.push("Run /skill:basic-memory-pi-setup to choose an explicit project.");
if (!workspaceTrusted) {
lines.push("To enable workspace automation, trust this workspace by setting BASIC_MEMORY_PI_TRUST_WORKSPACE=1 in Pi's environment.");
}
return lines.join("\n");
}

export function recallFenceFor(content: string): string {
const backtickRuns = content.match(/`+/g) ?? [];
const longestRun = backtickRuns.reduce((longest, run) => Math.max(longest, run.length), 0);
Expand Down Expand Up @@ -455,16 +478,7 @@ export default function basicMemoryPi(pi: ExtensionAPI): void {
notify(ctx, configError, "error");
return;
}
const lines = [
`transport: ${cfg.transport}`,
`bm: ${(cfg.bmCommand ?? [cfg.bmPath]).join(" ")}`,
`project: ${cfg.projectId ? `id:${cfg.projectId}` : cfg.project ?? "default"}`,
`capture folder: ${cfg.captureFolder}`,
`auto recall: ${cfg.autoRecall ? "on" : "off"}`,
`auto capture: ${cfg.autoCapture ? "on" : "off"}`,
`hook flow: ${cfg.useHookFlow ? "on" : "off"}`,
];
notify(ctx, lines.join("\n"), "info");
notify(ctx, formatStatus(cfg, process.env.BASIC_MEMORY_PI_TRUST_WORKSPACE === "1"), "info");
},
});

Expand Down
5 changes: 3 additions & 2 deletions integrations/pi/skills/basic-memory-pi-setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,15 @@ pi install npm:pi-mcp-adapter
}
```

6. Run `/bm-status`, then `/bm-recall setup` or `/bm-capture Pi setup checkpoint` to verify the path.
6. If the user wants automatic recall/capture or MCP tools, confirm they trust this workspace's project mapping, then set `BASIC_MEMORY_PI_TRUST_WORKSPACE=1` in the environment used to launch Pi. A project mapping alone does not enable automation. Manual `/bm-recall` and `/bm-capture` remain available with an explicit mapping without this trust setting.
7. Run `/bm-status` and check the effective auto recall/capture state, then `/bm-recall setup` or `/bm-capture Pi setup checkpoint` to verify the path.

## Defaults and escape hatches

- CLI transport is the default because it only requires `bm` on PATH.
- For local Basic Memory development in a trusted workspace, set `BASIC_MEMORY_PI_TRUST_BM_COMMAND=1` and use `bmCommand` as an argv array such as `["uv", "run", "--project", "/path/to/basic-memory", "basic-memory"]`; it overrides `bmPath` without using a shell.
- Hook flow is on by default so Pi uses the shared Basic Memory lifecycle contract.
- Automatic recall and capture are on by default once a project is configured.
- Automatic recall and capture are on by default once a project is configured and workspace trust is enabled.
- Set `BASIC_MEMORY_PI_TRUST_WORKSPACE=1` only after the user confirms this workspace should use its Basic Memory project mapping automatically; otherwise manual `/bm-recall` and `/bm-capture` still work.
- Set `autoRecall: false`, `autoCapture: false`, or `useHookFlow: false` if the user wants quieter behavior.

Expand Down
40 changes: 40 additions & 0 deletions integrations/pi/test/status.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import assert from "node:assert/strict";
import test from "node:test";

import { parseConfig } from "../extensions/config.ts";
import { formatStatus } from "../extensions/index.ts";

test("unconfigured status never implies ambient default routing or enabled automation", () => {
for (const trusted of [false, true]) {
const status = formatStatus(parseConfig(), trusted);
assert.match(status, /project: unconfigured/);
assert.match(status, /auto recall: blocked \(no project mapping\)/);
assert.match(status, /auto capture: blocked \(no project mapping\)/);
assert.match(status, /\/skill:basic-memory-pi-setup/);
assert.doesNotMatch(status, /project: default/);
}
});

test("untrusted workspace names the trust step even with a configured project", () => {
const status = formatStatus(parseConfig({ project: "research" }), false);
assert.match(status, /project: research/);
assert.match(status, /workspace trust: off/);
assert.match(status, /auto recall: blocked \(workspace not trusted\)/);
assert.match(status, /auto capture: blocked \(workspace not trusted\)/);
assert.match(status, /BASIC_MEMORY_PI_TRUST_WORKSPACE=1/);
});

test("trusted mapping reports effective settings and respects project ID precedence", () => {
const status = formatStatus(parseConfig({ project: "research", projectId: "project-id", autoCapture: false }), true);
assert.match(status, /project: id:project-id/);
assert.match(status, /auto recall: on/);
assert.match(status, /auto capture: off \(configured\)/);
assert.doesNotMatch(status, /blocked|To enable workspace automation/);
});

test("explicitly disabled automation stays off regardless of trust", () => {
const status = formatStatus(parseConfig({ autoRecall: false, autoCapture: false, useHookFlow: false }), false);
assert.match(status, /auto recall: off \(configured\)/);
assert.match(status, /auto capture: off \(configured\)/);
assert.match(status, /hook flow: off/);
});
Loading