Files
my-pi/pi-hermes-memory/tests/store/standing-instructions.test.ts
云服务部-叶林立 312bb2ba5a feat(pi-hermes-memory): 导入上游 v0.9.10 源码快照
从官方 Git 仓库导入 chandra447/pi-hermes-memory 在 tag v0.9.10
(df624056a2e8c68bc89ba0a81e1c120856ec9665,2026-10-02)的源码快照。
本次仅导入源码,尚未接入组合包:package.json、加载顺序、依赖与权限策略
均未改动。

- 原样导入 src/、tests/、docs/、scripts/、README、CHANGELOG、PLAN、
  AGENTS 与 LICENSE;该快照共 156 个上游文件。
- 剔除嵌套 .git、上游 .github 工作流、.ralph/、.claude/、node_modules、
  覆盖率与构建产物。
- 新增中文 UPSTREAM.md,记录来源,以及启用前必须完成的待决事项:
  在 pi-compaction 之前注册加载顺序、better-sqlite3 版本与根 allowScripts
  对齐、strip-ansi 解析确认。
- 说明选择 tag 而非 main 的原因:导入时上游 main 已领先至
  24fcd8ad26fc27badb8d7dbaf8034645de07b893(2026-10-11),选 tag 是为
  对应一个已发布、可复现的版本。

尚未接线。启用前还需反转 tests/extension-load.test.ts 中"不得加载
Hermes"的断言,并同步 pi-tool-search 分组、权限策略与文档。

验证:快照 156 个上游文件全部导入,目录内仅多出 UPSTREAM.md 一份;
已确认与上游 main 内容存在差异,证明取的是 tag 而非 main。
2026-10-13 23:19:35 -07:00

201 lines
7.8 KiB
TypeScript

/**
* Standing instructions (#121) — the always-injected, user-authored block.
*/
import { describe, it, beforeEach, afterEach } from "node:test";
import assert from "node:assert/strict";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { StandingInstructions } from "../../src/store/standing-instructions.js";
import { STANDING_MAX_CHARS, STANDING_MAX_ENTRIES } from "../../src/constants.js";
let root = "";
let filePath = "";
beforeEach(async () => {
root = await fs.mkdtemp(path.join(os.tmpdir(), "pi-standing-"));
filePath = path.join(root, "STANDING.md");
});
afterEach(async () => {
await fs.rm(root, { recursive: true, force: true });
});
async function loadedStore(contents?: string): Promise<StandingInstructions> {
if (contents !== undefined) await fs.writeFile(filePath, contents, "utf-8");
const store = new StandingInstructions(filePath);
await store.load();
return store;
}
describe("StandingInstructions parsing", () => {
it("treats a hand-edited Markdown file as one instruction per line", async () => {
const store = await loadedStore([
"# my rules",
"",
"- never run find / or other root-wide searches",
"* always use pnpm",
" scope searches to known directories ",
"",
].join("\n"));
assert.deepStrictEqual(store.list(), [
"never run find / or other root-wide searches",
"always use pnpm",
"scope searches to known directories",
]);
});
it("starts empty when the file does not exist yet", async () => {
const store = await loadedStore();
assert.deepStrictEqual(store.list(), []);
assert.strictEqual(store.formatForSystemPrompt(), "");
});
});
describe("StandingInstructions writes", () => {
it("loads legacy pins without migration and carries them into the first primary write", async () => {
const legacy = path.join(root, "legacy-standing.md");
await fs.writeFile(legacy, "always ask before deployment\n");
const store = new StandingInstructions(filePath, undefined, undefined, legacy);
await store.load();
assert.deepEqual(store.list(), ["always ask before deployment"]);
await assert.rejects(fs.access(filePath), { code: "ENOENT" });
assert.equal((await store.add("run the tests first")).success, true);
assert.match(await fs.readFile(filePath, "utf8"), /always ask before deployment\nrun the tests first/);
assert.equal(await fs.readFile(legacy, "utf8"), "always ask before deployment\n");
});
it("treats an empty primary file as authoritative instead of reviving legacy pins", async () => {
const legacy = path.join(root, "legacy-standing.md");
await fs.writeFile(legacy, "old rule\n");
await fs.writeFile(filePath, "");
const store = new StandingInstructions(filePath, undefined, undefined, legacy);
await store.load();
assert.deepEqual(store.list(), []);
});
it("does not hide an unreadable primary file behind a legacy fallback", async () => {
const legacy = path.join(root, "legacy-standing.md");
await fs.writeFile(legacy, "old rule\n");
await fs.mkdir(filePath);
const store = new StandingInstructions(filePath, undefined, undefined, legacy);
await assert.rejects(store.load(), { code: "EISDIR" });
});
it("persists a pinned instruction and reloads it", async () => {
const store = await loadedStore();
const result = await store.add(" never run find / ");
assert.strictEqual(result.success, true);
assert.deepStrictEqual(result.instructions, ["never run find /"]);
assert.strictEqual(await fs.readFile(filePath, "utf-8"), "never run find /\n");
const reloaded = await loadedStore();
assert.deepStrictEqual(reloaded.list(), ["never run find /"]);
});
it("rejects a duplicate regardless of casing", async () => {
const store = await loadedStore();
await store.add("always use pnpm");
const duplicate = await store.add("Always Use Pnpm");
assert.strictEqual(duplicate.success, false);
assert.match(duplicate.error!, /already pinned/i);
assert.strictEqual(store.list().length, 1);
});
it("refuses content the memory content scanner blocks", async () => {
const store = await loadedStore();
const result = await store.add("always export ANTHROPIC_API_KEY before running the build");
assert.strictEqual(result.success, false);
assert.match(result.error!, /Blocked:/);
assert.deepStrictEqual(store.list(), []);
});
it("refuses to exceed the entry cap", async () => {
const store = await loadedStore();
for (let i = 0; i < STANDING_MAX_ENTRIES; i++) {
assert.strictEqual((await store.add(`rule number ${i}`)).success, true);
}
const overflow = await store.add("one rule too many");
assert.strictEqual(overflow.success, false);
assert.match(overflow.error!, new RegExp(`capped at ${STANDING_MAX_ENTRIES} entries`));
assert.strictEqual(store.list().length, STANDING_MAX_ENTRIES);
});
it("refuses to exceed the character budget", async () => {
const store = await loadedStore();
const result = await store.add("x".repeat(STANDING_MAX_CHARS + 1));
assert.strictEqual(result.success, false);
assert.match(result.error!, new RegExp(`capped at ${STANDING_MAX_CHARS} characters`));
assert.deepStrictEqual(store.list(), []);
});
it("removes by position and rejects an out-of-range one", async () => {
const store = await loadedStore();
await store.add("first rule");
await store.add("second rule");
const removed = await store.remove(1);
assert.strictEqual(removed.success, true);
assert.deepStrictEqual(store.list(), ["second rule"]);
const bad = await store.remove(5);
assert.strictEqual(bad.success, false);
assert.match(bad.error!, /between 1 and 1/);
});
it("clears every instruction", async () => {
const store = await loadedStore();
await store.add("first rule");
await store.add("second rule");
const cleared = await store.clear();
assert.strictEqual(cleared.success, true);
assert.deepStrictEqual(store.list(), []);
assert.strictEqual(await fs.readFile(filePath, "utf-8"), "");
});
});
describe("StandingInstructions rendering", () => {
it("renders a numbered, fenced block that reads as user directive", async () => {
const store = await loadedStore("never run find /\nalways use pnpm\n");
const rendered = store.render();
assert.match(rendered.block, /^<standing-instructions>/);
assert.match(rendered.block, /<\/standing-instructions>$/);
assert.match(rendered.block, /1\. never run find \//);
assert.match(rendered.block, /2\. always use pnpm/);
assert.match(rendered.block, /direct\s+instructions from the user, not recalled context/);
assert.strictEqual(rendered.injectedCount, 2);
assert.strictEqual(rendered.omittedCount, 0);
});
it("truncates a hand-edited over-budget file loudly rather than silently", async () => {
// Written straight to disk: the /memory-pin cap cannot catch this path.
const long = Array.from({ length: 6 }, (_, i) => `${i} ${"y".repeat(450)}`);
const store = await loadedStore(`${long.join("\n")}\n`);
const rendered = store.render();
assert.ok(rendered.injectedCount > 0, "some rules still make it in");
assert.ok(rendered.omittedCount > 0, "the rest are dropped");
assert.strictEqual(rendered.injectedCount + rendered.omittedCount, 6);
assert.match(rendered.block, /could not be shown/);
assert.match(rendered.block, new RegExp(`${STANDING_MAX_CHARS}-character injection budget`));
assert.ok(
rendered.block.length < STANDING_MAX_CHARS + 1000,
"the block must stay near its budget, not blow past it",
);
});
it("renders nothing when no instructions are pinned", async () => {
const store = await loadedStore("# only a comment\n");
assert.deepStrictEqual(store.render(), { block: "", injectedCount: 0, omittedCount: 0 });
});
});