Skip to content

Extension CommandContext exposes only an absolute pm_root, so extension output leaks host paths that core commands never emit #685

Closed

Description

@unbraind

Filed from real usage while building a merge-preview extension against @unbrained/pm-cli 2026.7.24-3.

The inconsistency

Core pm commands are careful never to put host filesystem paths in agent-facing output. Verified in a scratch workspace at /tmp/.../divtest:

pm context --json           -> host-path occurrences: 0
pm stats --json             -> host-path occurrences: 0
pm list --json              -> host-path occurrences: 0
pm activity --from -7d --json -> host-path occurrences: 0

That is a genuinely valuable property: pm output is portable, diffable, and safe to paste between agents, commit into a repo, or attach to a PR.

Extensions cannot match it. The only workspace location an extension receives is context.pm_root, and it is the resolved absolute path — per dist/sdk/testing.d.ts, "Resolved pm workspace root forwarded as context.pm_root". There is no repo-relative counterpart anywhere on the context object, and pm context --json exposes no root/workspace/path field an extension could read instead.

So any extension that renders its workspace into output emits something like:

{ "workspace": "/tmp/claude-1000/-home-steve-container-pm-cli/e78b6f60/scratchpad/divtest/.agents/pm" }

where every core command would have emitted .agents/pm or nothing at all.

Why this matters beyond cosmetics

  1. It leaks the host path into shared artifacts. Extension output is exactly the kind of thing that gets committed, posted to a PR, or handed to another agent. /home/<user>/... in a published changelog or brief is a privacy leak, and it is one that core commands are already designed to avoid.
  2. It breaks portability of recorded commands. Agents record the command and its output as evidence. An absolute path pins that record to one machine, so a teammate or a CI runner re-running it gets a different answer or none.
  3. Every extension author reimplements the same non-trivial resolution. To be well-behaved, an extension must locate the git root itself (git rev-parse --show-toplevel), relativize pm_root against it, normalize separators for Windows, and reject the out-of-repo and equal-to-repo-root cases. I wrote exactly that (pmRootRelFromCtx) and needed two review rounds to get the edge cases right — a sibling directory named ..cache/pm was rejected by a naive startsWith(".."), and a pm root equal to the repo root produced an empty prefix that silently matched nothing. Each extension re-deriving this will get a different subset of those wrong.

Suggested fix

Add the relative form to the context the CLI already computes, e.g.:

interface CommandContext {
  pm_root: string;       // absolute, unchanged
  pm_root_rel?: string;  // POSIX-separated, relative to the repo root when inside one
  repo_root?: string;    // absolute repo root, or undefined when not in a repo
}

The CLI already knows all three at dispatch time, so this costs nothing to provide and lets extensions default to the portable form. If adding context fields is undesirable, an exported SDK helper (resolvePmRootRelative(pmRoot, cwd)) with the edge cases handled once would solve the duplication, though it would not nudge authors toward the portable default the way a context field does.

Either way the goal is that an extension can be host-path-free as easily as core already is.

Reproducing

mkdir /tmp/pmroot-demo && cd /tmp/pmroot-demo && git init -q
pm init --pm-path .agents/pm
# in any extension command handler, log context.pm_root:
#   -> /tmp/pmroot-demo/.agents/pm   (absolute)
# compare: pm context --json contains no absolute path at all

Environment

  • @unbrained/pm-cli 2026.7.24-3
  • Linux, Node 22

Happy to send a PR if the pm_root_rel + repo_root context shape is the direction you would want.

Activity

  1. coderabbitai commented on Jul 24, 2026

    @coderabbitai
    🔗 Related PRs

    #73 - Fix nightly macOS realpath + Windows .cmd EINVAL and dedup extension-policy override builder [closed]
    #142 - Guard pm tracker path targeting for agents [closed]
    #302 - Optimize npm package SDK installs [merged]
    #428 - Harden SDK extension runtime packaging [merged]
    #561 - SDK: unify extension input and output host contracts [merged]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  2. unbraind commented on Jul 25, 2026

    @unbraind
    OwnerAuthor

    pm tracker item:\n- pm-j4ac9a — GH-685: extension CommandContext needs portable source and tracker workspace coordinates

  3. unbraind commented on Jul 25, 2026

    @unbraind
    OwnerAuthor

    Fixed by PR #693: extension CommandContext now exposes portable source workspace/repository/tracker-relative coordinates instead of requiring host-absolute pm_root output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions