Files
my-pi/pi-hermes-memory/docs/0.2/PLAN.md
T
云服务部-叶林立 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

12 KiB

v0.2.0 Implementation Plan — Skills + Smart Curation

Goal: Close the two biggest Hermes gaps — procedural memory (skills) and intelligent memory management (auto-consolidation, correction detection, tool-call-aware nudges).

Implementation Order

Epic 2 (Auto-Consolidation)  → standalone, modifies MemoryStore.add()
Epic 3 (Correction Detection) → standalone, new handler
Epic 4 (Tool-Call Nudge)      → modifies background-review.ts
Epic 1 (Skill Tool)           → largest: new store + tool + handlers
Epic 5 (Docs + Release)       → depends on all above

Epics 2, 3, 4 are independent but done sequentially to avoid merge conflicts in shared files (types.ts, config.ts, constants.ts, index.ts).


Epic 2: Auto-Consolidation

Problem: When add() exceeds char limit, we return an error. Hermes auto-consolidates.

New Files

src/handlers/auto-consolidate.ts (~120 lines)

export async function triggerConsolidation(
  pi: ExtensionAPI,
  store: MemoryStore,
  target: "memory" | "user",
  signal?: AbortSignal,
): Promise<ConsolidationResult>
  • Builds prompt from CONSOLIDATION_PROMPT + current entries for the target
  • Calls pi.exec("pi", ["-p", "--no-session", prompt], { signal, timeout: 60000 })
  • Returns { consolidated: true } on success, { consolidated: false, error } on failure

src/handlers/consolidate-command.ts (~30 lines)

  • Registers /memory-consolidate via pi.registerCommand()
  • Runs consolidation for both targets, reports via ctx.ui.notify()

tests/handlers/auto-consolidate.test.ts (~120 lines)

Modified Files

src/constants.ts — Add CONSOLIDATION_PROMPT

src/types.ts — Add autoConsolidate: boolean to MemoryConfig; add ConsolidationResult interface

src/config.ts — Add autoConsolidate: true default + parsing

src/store/memory-store.ts — Key changes:

  • add() becomes async (returns Promise<MemoryResult>)
  • Add setConsolidator() method for dependency injection (avoids circular import)
  • When over limit + consolidator set: call consolidator, reload from disk (await this.loadFromDisk()), then retry once
  • Critical: The pi.exec() child process modifies files on disk. The parent's in-memory arrays become stale after consolidation. We MUST reload before retrying add() or the retry will overwrite consolidated entries with stale data.

src/tools/memory-tool.ts — await store.add(target, content) (line ~58)

src/index.ts — Wire consolidator + register command

Test migration: Making add() async means all existing tests calling store.add() must use await. Without await, tests get a Promise object instead of MemoryResult, causing assertion failures. Update all store.add() calls in tests/store/memory-store.test.ts to await store.add().

Key Decision: Consolidator Injection via Setter

MemoryStore cannot import from handlers (circular). Instead, index.ts injects a consolidator function via store.setConsolidator() after both store and pi are available.

Key Decision: No memoryDirPath Getter

SkillStore receives its directory path directly from config (config.memoryDir + "/skills/") in index.ts. No need to expose MemoryStore internals.


Epic 3: Correction Detection + Immediate Save

Problem: User says "no, don't do that" — we only save it 8 turns later at the next nudge. Hermes detects immediately.

New Files

src/handlers/correction-detector.ts (~100 lines)

export function setupCorrectionDetector(
  pi: ExtensionAPI,
  store: MemoryStore,
  config: MemoryConfig,
): void

Design:

  1. On message_end (role=user): check text against CORRECTION_PATTERNS, set pendingCorrection = true
  2. On turn_end: if pendingCorrection, trigger pi.exec() with CORRECTION_SAVE_PROMPT + recent messages + current memory
  3. Rate limit: turnsSinceLastCorrection >= 3 and !correctionInProgress

Why turn_end, not message_end: We need the full context (user correction + what agent said wrong) for the save prompt.

tests/handlers/correction-detector.test.ts (~150 lines)

Modified Files

src/constants.ts — Add CORRECTION_SAVE_PROMPT and CORRECTION_PATTERNS (regex array)

src/types.ts — Add correctionDetection: boolean to MemoryConfig

src/config.ts — Add correctionDetection: true default + parsing

src/index.ts — Wire setupCorrectionDetector()

Correction Patterns (Two-Pass Filter)

Patterns are split into strong (high confidence, trigger immediately) and weak (need a directive clause to confirm).

Strong patterns (always trigger):

/don'?t do that/i, /not like that/i,
/^I said\b/i, /^I told you\b/i, /we already discussed/i,
/^please don'?t/i, /^that'?s not what I/i

Weak patterns (only trigger if followed by a directive — verb or "the/that/this"):

/^no[,.\s!]/i, /^wrong[,.\s!]/i, /^actually[,.\s]/i, /^stop[,.\s!]/i

Negative patterns (suppress trigger even if a positive pattern matches):

/^no worries/i, /^no problem/i, /^no thanks/i, /^no need/i,
/^actually.{0,10}(looks? great|perfect|good|correct|right)/i,
/^stop.{0,5}(there|here|for now)/i

This eliminates false positives like "no worries, I'll handle it" and "actually, that looks great" while still catching "no, don't use npm" and "actually, use yarn instead".


Epic 4: Tool-Call-Aware Nudge

Problem: Nudge is purely turn-count based. Complex tasks with many tool calls generate more valuable memories.

Modified Files

src/types.ts — Add nudgeToolCalls: number to MemoryConfig

src/config.ts — Add nudgeToolCalls: 15 default + parsing

src/handlers/background-review.ts — Key changes:

  • Count tool-use entries from ctx.sessionManager.getBranch() at turn_end time (robust — no unknown event names)
  • Change trigger to OR logic: turnsSinceReview >= nudgeInterval || toolCallsSinceReview >= nudgeToolCalls
  • Reset both counters on review

tests/handlers/background-review.test.ts — Add tool-call trigger tests

Key Decision: Count from Branch, Not Events

Rather than depending on unknown Pi event names like tool_end, count tool-use entries from ctx.sessionManager.getBranch() at turn_end time. More robust and testable.


Epic 1: Skill Tool + Procedural Memory

Problem: COMBINED_REVIEW_PROMPT asks about skills but there's no skill tool. This is the single highest-leverage change.

New Files

src/store/skill-store.ts (~250 lines)

export class SkillStore {
  constructor(private skillsDir: string) {}
  async loadIndex(): Promise<SkillIndex[]>
  async loadSkill(fileName: string): Promise<SkillDocument | null>
  async create(name: string, description: string, body: string): Promise<SkillResult>
  async patch(fileName: string, section: string, newContent: string): Promise<SkillResult>
  async edit(fileName: string, description: string, body: string): Promise<SkillResult>
  async delete(fileName: string): Promise<SkillResult>
  formatIndexForSystemPrompt(): string
}

Storage: ~/.pi/agent/memory/skills/ (isolated from user skills at ~/.pi/agent/skills/)

SKILL.md format:

---
name: debug-typescript-errors
description: Step-by-step approach to debugging TS errors in monorepos
version: 1
created: 2026-04-27
updated: 2026-04-27
---
## When to Use
## Procedure
## Pitfalls
## Verification

Frontmatter parsing: Simple regex (no yaml dependency). Split on ---, parse key-value pairs.

File naming: slugify(name) + ".md" — lowercase, replace non-alphanum with -, collapse dashes.

src/tools/skill-tool.ts (~180 lines)

  • Registered via pi.registerTool() with actions: create, view, patch, edit, delete
  • Content scanning on all writes via scanContent()

src/handlers/skill-auto-trigger.ts (~80 lines)

  • Track tool calls per turn
  • When turn completes with 8+ tool calls (not 5 — a typical read→bash→edit→bash→read is already 5), trigger skill extraction via pi.exec()
  • Additionally require at least 2 distinct tool types in the turn (e.g., read + bash, not just 8 reads) to filter trivial multi-call turns
  • Rate limit: max 1 auto-trigger per session

src/handlers/skills-command.ts (~50 lines)

  • /memory-skills command listing all skills

Test files: tests/store/skill-store.test.ts, tests/tools/skill-tool.test.ts, tests/handlers/skill-auto-trigger.test.ts

Modified Files

src/constants.ts — Add SKILL_TOOL_DESCRIPTION, DEFAULT_SKILL_TRIGGER_TOOL_CALLS (= 8); update COMBINED_REVIEW_PROMPT:

export const COMBINED_REVIEW_PROMPT = `Review the conversation above and consider two things:

**Memory**: Has the user revealed things about themselves — their persona, desires, preferences, or personal details? Has the user expressed expectations about how you should behave, their work style, or ways they want you to operate? If so, save using memory_add.

**Skills**: Was a complex, non-trivial approach used to complete a task — one that required trial and error, multiple tool calls, or changing course? If so, save a reusable procedure using the skill tool with action 'create'. Include: when to use it, step-by-step procedure, pitfalls to avoid, and how to verify success.

Only act if there's something genuinely worth saving. If nothing stands out, just say 'Nothing to save.' and stop.`;

Note on pi.exec() child tools: The child pi process loads the same installed extension, so it has access to the memory write tools and skill_manage. This is the same mechanism that makes the existing memory tools work in background review.

src/index.ts — Wire SkillStore, registerSkillTool, setupSkillAutoTrigger, registerSkillsCommand; inject skill index into system prompt at before_agent_start. Pass config.memoryDir + "/skills/" directly to SkillStore constructor (no memoryDirPath getter needed).

Progressive Disclosure

  • System prompt: Skill index only (name + description per skill, ~3K tokens max)
  • On demand: Agent calls skill tool with action view to load full content
  • Frozen snapshot: Index captured at session_start, same as memory snapshot

Key Decision: Frozen Snapshot for Skills

Skill index is captured at session_start and injected at before_agent_start. New skills created mid-session appear in the index on next session. This preserves Pi's prompt cache.


Epic 5: Documentation & Release

  • Update README.md with new features, config options, commands
  • Update docs/ROADMAP.md — mark v0.2 complete
  • Bump package.json version to 0.2.0
  • npm run check passes, all tests pass
  • Tag v0.2.0

File Change Summary

New Files (12)

File Lines Epic
src/handlers/auto-consolidate.ts ~120 2
src/handlers/consolidate-command.ts ~30 2
src/handlers/correction-detector.ts ~100 3
src/store/skill-store.ts ~250 1
src/tools/skill-tool.ts ~180 1
src/handlers/skill-auto-trigger.ts ~80 1
src/handlers/skills-command.ts ~50 1
tests/handlers/auto-consolidate.test.ts ~120 2
tests/handlers/correction-detector.test.ts ~150 3
tests/store/skill-store.test.ts ~200 1
tests/tools/skill-tool.test.ts ~100 1
tests/handlers/skill-auto-trigger.test.ts ~80 1

Modified Files (8)

File Epic(s)
src/types.ts 2, 3, 4
src/constants.ts 1, 2, 3, 4
src/config.ts 2, 3, 4
src/store/memory-store.ts 1, 2
src/tools/memory-tool.ts 2
src/handlers/background-review.ts 4
src/index.ts 1, 2, 3, 4
tests/handlers/background-review.test.ts 4

Verification

After each epic:

  1. npm run check — zero type errors
  2. npm test — all tests pass
  3. Manual test: pi -e ./src/index.ts — verify the feature works in a live session

Final: 4. Full regression: all 119 existing tests + new tests pass 5. Tag v0.2.0