Skip to content

feat: PAM 0.4.0 — Claude & Kimi agent layers, MCP server#5

Merged
Yehonal merged 3 commits into
NestDevLab:mainfrom
StefanoGuerrini:feat/pam-0.4.0
Jun 2, 2026
Merged

feat: PAM 0.4.0 — Claude & Kimi agent layers, MCP server#5
Yehonal merged 3 commits into
NestDevLab:mainfrom
StefanoGuerrini:feat/pam-0.4.0

Conversation

@StefanoGuerrini

Copy link
Copy Markdown
Contributor

Summary

PAM 0.4.0 ships the optional agent layer end-to-end:

  • MCP server (tools/pam-mcp-server.mjs) exposes 15 typed tools over stdio: pam_version, memory_state, memory_list/read/search, graph_query/stats/validate/reindex, memory_audit, memory_propose_edit, memory_append, memory_apply_proposal, maintenance_config/run.
  • Subagents: curator (read-mostly hygiene auditor, emits proposals only) and scribe (session-end closeout, append-only via memory_append).
  • Claude Code plugin (.claude-plugin/plugin.json + .claude/{agents,commands}/ + hooks/) wires the MCP server, agents, slash commands, and lifecycle hooks into a single installable unit. Slash commands: /pam:dream, /pam:status, /pam:explain, /pam:enable-status-line.
  • Statusline + hooks: SessionStart freshness/proposal nag, PostToolUse append counter, statusline showing graph health and session activity.

The markdown + JSONL contract is unchanged; existing 0.3.0 graph-v1 workspaces work without modification.

Notable correctness fixes in this commit

  • memory_apply_proposal now uses the validated input proposalId for the archive path (not record.proposalId), closing a hand-crafted-artifact traversal risk. Archive is written before the target so an interrupted apply leaves the proposal recoverable.
  • /dream calls graph_reindex (not graph_validate) to actually refresh memory/graph/catalog.json.
  • Statusline and session-start hook exclude *.applied.json from the pending-proposal count.
  • hooks/post-memory-append.sh rejects session IDs containing /, \\, or .. before using them as a filename.

Test plan

  • npm test (66/66 subtests pass)
  • npm run mcp:smoke (MCP server starts cleanly)
  • Plugin loads in Claude Code; slash commands work
  • pr-reviewer self-review: APPROVE
  • Reviewer: spot-check the new MCP write surface
  • Reviewer: confirm the curator/scribe agent prompts read sensibly

@StefanoGuerrini StefanoGuerrini force-pushed the feat/pam-0.4.0 branch 2 times, most recently from 6faeec6 to c07bb15 Compare May 22, 2026 19:01

@Yehonal Yehonal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. I would not approve this yet: the Claude/MCP layer is useful as a standalone base, but there are a few correctness issues that should be fixed before merge.

Findings:

  1. Blocking: the Claude plugin appears to use the plugin repository as the PAM workspace.

.claude-plugin/plugin.json starts the MCP server from ${CLAUDE_PLUGIN_ROOT}/tools/pam-mcp-server.mjs, while tools/lib/workspace.mjs defaults the workspace from the script path. When installed as a plugin, this means pam_version, memory_append, /pam:dream, curator/scribe, etc. can operate on the plugin copy instead of the user project. Please pass the actual Claude project directory as --workspace, or change the hosted-server default to resolve to the opened project/current working directory instead of the script location.

  1. High: memory_append does not enforce protectedPaths.

tools/lib/memory-append.mjs accepts any config.managedLogs[].source inside the workspace and writes to it without checking config.protectedPaths. This contradicts the MCP docs, which say there is no MCP write path for protected files such as AGENTS.md, CLAUDE.md, and memory/agent-memory/. A wrong or malicious config can route appends into protected paths. Please apply the same protected-path guard before writing append entries.

  1. Medium: unified diff application is fragile for multi-hunk patches.

tools/lib/memory-proposals.mjs applies hunks in forward order using the original oldStart values against already-mutated content. If an earlier hunk adds/removes lines, later hunks can fail with a mismatch or target the wrong location. Please apply hunks from bottom to top, or track a cumulative line delta while applying them.

  1. Low: /pam:status counts already-applied proposals as pending.

.claude/commands/status.md uses ls memory/maintenance/proposals/*.json, which includes *.applied.json. Other status surfaces exclude applied proposals correctly. Please filter those out, e.g. with find ... -name '*.json' ! -name '*.applied.json'.

Validation I ran locally:

  • npm test: 66/66 passed
  • npm run memory:graph:validate: ok
  • npm run mcp:smoke: ok
  • git diff --check origin/main...HEAD: ok

StefanoGuerrini added a commit to StefanoGuerrini/portable-agent-memory that referenced this pull request May 23, 2026
…date Claude layer into tools/claude/

Review fixes:
- fix(workspace): default workspace root to process.cwd() so MCP server
  operates on the user's project when launched as a Claude Code plugin
- fix(memory-append): enforce protectedPaths before writing; reject
  appends to AGENTS.md, memory/agent-memory/, memory/sources/, etc.
- fix(memory-proposals): apply unified-diff hunks bottom-to-top to handle
  multi-hunk patches with line count changes correctly
- fix(status command): exclude *.applied.json from pending proposal count

Refactor:
- Move all Claude-specific files from root into tools/claude/:
  - .claude/{agents,commands} -> tools/claude/{agents,commands}
  - hooks/ -> tools/claude/hooks/
  - templates/pam-claude-layer/ -> tools/claude/templates/
  - docs/{curator,scribe,pam-claude-layer}.md -> tools/claude/docs/
  - tools/install-pam-statusline.mjs -> tools/claude/install-statusline.mjs
- Update root .claude-plugin/plugin.json with explicit paths
- Update CHANGELOG.md, package.json scripts, and all internal references

Tests: 68/68 pass (66 existing + 2 new)
@StefanoGuerrini

Copy link
Copy Markdown
Contributor Author

Hi @Yehonal, thanks for the thorough review. All four findings have been addressed in the latest push:

  1. Blocking (workspace root): Changed DEFAULT_WORKSPACE_ROOT in tools/lib/workspace.mjs from __dirname-based resolution to process.cwd(). This ensures the MCP server operates on the opened project when launched by the Claude Code plugin, rather than on the plugin installation cache.

  2. High (protectedPaths in memory_append): tools/lib/memory-append.mjs now checks config.protectedPaths before writing. A managed log that routes to a protected path (e.g. AGENTS.md, memory/agent-memory/) is rejected with a clear error. Added a test to cover this.

  3. Medium (multi-hunk diff fragility): tools/lib/memory-proposals.mjs now sorts hunks by oldStart descending (bottom-to-top) before applying them. This prevents line-drift mismatches when earlier hunks add or remove lines. Added a multi-hunk test with two deletions to verify correctness.

  4. Low (status counting applied proposals): Updated the shell snippet in tools/claude/commands/status.md to use find ... ! -name '*.applied.json', so already-applied proposal archives are excluded from the pending count.

Additional refactor: All Claude-specific artifacts have been consolidated under tools/claude/ (agents, commands, hooks, templates, docs, and the statusline installer). The root .claude-plugin/plugin.json now uses explicit commands / agents / hooks paths so Claude Code can discover them from the new location. This keeps the root clean and makes room for future tools/codex/ and tools/kimi/ layers.

Verification:

  • npm test: 68/68 passed (66 existing + 2 new)
  • npm run memory:graph:validate: ok
  • npm run mcp:smoke: ok
  • git diff --check: clean

Could you take another look when you have a moment?

@StefanoGuerrini StefanoGuerrini requested a review from Yehonal May 23, 2026 17:00
@StefanoGuerrini

Copy link
Copy Markdown
Contributor Author

Bonus: Added a tools/kimi/ integration layer in the latest push.

Kimi Code CLI has supported MCP since late 2025. The new tools/kimi/install-mcp.mjs registers PAM with Kimi by writing an absolute-path entry into ~/.kimi/mcp.json:

node tools/kimi/install-mcp.mjs --apply
kimi mcp test pam
  • Dry-run by default, --apply to write, --uninstall --apply to remove
  • Backs up existing ~/.kimi/mcp.json
  • Update-safe: git pull updates the server without touching Kimi's config

Docs live in tools/kimi/docs/pam-kimi-layer.md and the MCP server doc now covers Kimi too.

@StefanoGuerrini StefanoGuerrini changed the title feat: release PAM 0.4.0 with Claude Code plugin feat: PAM 0.4.0 — Claude & Kimi agent layers, MCP server, and review fixes May 23, 2026
@StefanoGuerrini StefanoGuerrini changed the title feat: PAM 0.4.0 — Claude & Kimi agent layers, MCP server, and review fixes feat: PAM 0.4.0 — Claude & Kimi agent layers, MCP server May 23, 2026

@Yehonal Yehonal left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to merge from my side. The symlink containment issue discussed in review appears to be pre-existing rather than introduced by this PR, so I would track it as a separate security follow-up unless this PR expands the production trust boundary.\n\nVerified locally: npm test passes (68/68), and git diff --check is clean.

@Yehonal

Yehonal commented May 27, 2026

Copy link
Copy Markdown
Contributor

Heads up: we split semantic migration enforcement into PR #8 so it can land before this feature branch.

After #8 merges, this PR should be rebased onto updated main and adjusted as the next semantic migration step:

  • keep this feature as PAM 0.4.0
  • add/rename the migration guide to start from 0.3.1, e.g. migrations/0.3.1-to-0.4.0-agent-layer.md
  • run npm run migrations:check in addition to the existing tests
  • keep package.json and memory/pam.version.json aligned at 0.4.0

This preserves the intended ordered upgrade chain for agents migrating older workspaces:
0.3.0 -> 0.3.1 -> 0.4.0 -> 0.4.1 -> 0.5.0.

PAM 0.4.0 ships the optional agent layer end-to-end: a local stdio MCP
server, the curator and scribe reference subagents, and a Claude Code
plugin that wires the MCP server, agents, slash commands, and hooks into
a single installable unit.

Runtime:
- MCP server (tools/pam-mcp-server.mjs + tools/lib/*) exposes 15 typed
  tools over stdio (pam_version, memory_state, memory_list/read/search,
  graph_query/stats/validate/reindex, memory_audit, memory_propose_edit,
  memory_append, memory_apply_proposal, maintenance_config/run).
- memory_append appends a dated section to a managed log, newest-first,
  validating the YYYY-MM-DD header contract.
- memory_apply_proposal re-validates path safety, re-applies the diff
  with drift detection, archives successful applies as
  <id>.applied.json, and writes the archive before mutating the target
  so an interrupted apply leaves the proposal recoverable.

Subagents:
- curator: read-mostly hygiene auditor; never mutates memory; emits
  proposals only.
- scribe: session-end closeout; append-only via memory_append.

Claude Code plugin layout:
- .claude-plugin/plugin.json declares the plugin and inlines mcpServers.
- .claude/commands/: dream, explain, status, enable-status-line
  (namespaced under /pam: by the plugin).
- .claude/agents/: curator, scribe.
- hooks/: SessionStart freshness check and PostToolUse memory_append
  counter.

UX:
- Statusline shows graph health, pending-proposal count, catalog age,
  and session appends. Pending count excludes *.applied.json.
- /dream calls graph_reindex (not graph_validate) to actually refresh
  memory/graph/catalog.json.
- /explain prints a one-screen legend with live values via an inlined
  shell block; no LLM reasoning per invocation.
- /enable-status-line toggles the statusline by renaming statusLine
  to/from _pamDisabledStatusLine. Reversible; never deletes.

Docs:
- docs/mcp-server.md, docs/curator-agent.md, docs/scribe-agent.md,
  docs/pam-claude-layer.md.

Notes:
- memory/maintenance/proposals/ is now gitignored; proposal artifacts
  are workspace-local audit trail and should not ship in clones.
…date Claude layer into tools/claude/

Review fixes:
- fix(workspace): default workspace root to process.cwd() so MCP server
  operates on the user's project when launched as a Claude Code plugin
- fix(memory-append): enforce protectedPaths before writing; reject
  appends to AGENTS.md, memory/agent-memory/, memory/sources/, etc.
- fix(memory-proposals): apply unified-diff hunks bottom-to-top to handle
  multi-hunk patches with line count changes correctly
- fix(status command): exclude *.applied.json from pending proposal count

Refactor:
- Move all Claude-specific files from root into tools/claude/:
  - .claude/{agents,commands} -> tools/claude/{agents,commands}
  - hooks/ -> tools/claude/hooks/
  - templates/pam-claude-layer/ -> tools/claude/templates/
  - docs/{curator,scribe,pam-claude-layer}.md -> tools/claude/docs/
  - tools/install-pam-statusline.mjs -> tools/claude/install-statusline.mjs
- Update root .claude-plugin/plugin.json with explicit paths
- Update CHANGELOG.md, package.json scripts, and all internal references

Tests: 68/68 pass (66 existing + 2 new)
- tools/kimi/install-mcp.mjs registers PAM with Kimi Code CLI by writing
  an absolute-path entry into ~/.kimi/mcp.json
- tools/kimi/templates/mcp.fragment.json is the config template
- tools/kimi/docs/pam-kimi-layer.md covers install, verify, uninstall,
  and ad-hoc --mcp-config-file usage
- npm run kimi:mcp:install shortcut
- docs/mcp-server.md and AGENT_BOOTSTRAP.md gain Kimi-specific sections

The installer mirrors the Claude statusline installer UX:
- dry-run by default, --apply to write
- --force to overwrite existing pam entry
- --uninstall to remove and restore backup
- backs up existing ~/.kimi/mcp.json before writing

Update-safe: absolute paths mean git pull updates the server code
without touching Kimi's config.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants