{"_id":"@alexleekt/pi-patch-manager","_rev":"2-a65207be5f99d3c20fe42af6d86639b2","name":"@alexleekt/pi-patch-manager","dist-tags":{"latest":"0.4.0"},"versions":{"0.3.0":{"name":"@alexleekt/pi-patch-manager","version":"0.3.0","keywords":["pi-package","pi-extension","pi","patch","patch-manager"],"author":{"name":"Alex Lee","email":"657215+alexleekt@users.noreply.github.com"},"license":"MIT","_id":"@alexleekt/pi-patch-manager@0.3.0","maintainers":[{"name":"alexleekt","email":"stratrix@gmail.com"}],"homepage":"https://github.com/alexleekt/pi-extensions/tree/main/packages/pi-patch-manager#readme","bugs":{"url":"https://github.com/alexleekt/pi-extensions/issues"},"pi":{"skills":["./skills"],"extensions":["./index.ts"]},"dist":{"shasum":"eb60eb49e5b1db68d53d8721375257eb456b8314","tarball":"https://registry.npmjs.org/@alexleekt/pi-patch-manager/-/pi-patch-manager-0.3.0.tgz","fileCount":8,"integrity":"sha512-Vs9Myvzqh8mzeuyVsQF8ZKkhWGNh0/lBL5zD/so1HkY3UjRcfdRs98NLzyLwaf+PLihYRZWO8HBFMaOH3Dwfwg==","signatures":[{"sig":"MEUCIE1T3S8FuQFemmxX110X/cIijn1H7JmgSyr7+zS5bbzEAiEAsqGgkz8nQrknSHzC7ACMfJJZ5sKLxx1xkWTzxTSPBV0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":83270},"type":"module","engines":{"node":">=23.6.0"},"gitHead":"b205296eb52d8124d5dc2516758f58ca8f37356d","scripts":{"test":"node test-integration.mjs","check":"tsc --noEmit"},"_npmUser":{"name":"alexleekt","email":"stratrix@gmail.com"},"repository":{"url":"git+https://github.com/alexleekt/pi-extensions.git","type":"git"},"_npmVersion":"11.19.0","description":"Safe inspection and management of Git patches for Pi packages.","directories":{},"_nodeVersion":"26.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.3","@earendil-works/pi-coding-agent":"0.80.6"},"peerDependencies":{"typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-patch-manager_0.3.0_1787954774349_0.5477539726248153","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@alexleekt/pi-patch-manager","version":"0.4.0","description":"Safe inspection and management of Git patches for Pi packages.","type":"module","license":"MIT","author":{"name":"Alex Lee","email":"657215+alexleekt@users.noreply.github.com"},"keywords":["pi-package","pi-extension","pi","patch","patch-manager"],"repository":{"type":"git","url":"git+https://github.com/alexleekt/pi-extensions.git"},"publishConfig":{"access":"public"},"homepage":"https://github.com/alexleekt/pi-extensions/tree/main/packages/pi-patch-manager#readme","bugs":{"url":"https://github.com/alexleekt/pi-extensions/issues"},"engines":{"node":">=23.6.0"},"pi":{"extensions":["./index.ts"],"skills":["./skills"]},"peerDependencies":{"@earendil-works/pi-ai":"*","@earendil-works/pi-coding-agent":"*","typebox":"*"},"devDependencies":{"@earendil-works/pi-coding-agent":"0.85.1","typescript":"^5.9.3"},"scripts":{"check":"tsc --noEmit","test":"node test-integration.mjs"},"gitHead":"c0dfe11921f6102731462fe2c2daf06702ba43b6","_id":"@alexleekt/pi-patch-manager@0.4.0","_nodeVersion":"24.20.0","_npmVersion":"11.19.0","dist":{"integrity":"sha512-RacpB6LHgy1I0mHZaOpGaPwTb4EgIcbgMY4i7QiCl5itNmT0b6fGfA6rA1viEPwGHJ9ZrlPPuP6tAX7N1PpLMg==","shasum":"7a726eefed77d35bf9bfef69bb60dfe0f00e1bec","tarball":"https://registry.npmjs.org/@alexleekt/pi-patch-manager/-/pi-patch-manager-0.4.0.tgz","fileCount":8,"unpackedSize":87831,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexleekt%2fpi-patch-manager@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDoDgKnbEYTqf3B/OYp2E1JcYdK9o3IPOil+lSX6n969gIhAK6EYZbf7s1rqCQembUQ8KxlapjLovXKotk7tL9Mz9SC"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:e9f9c2ed-02a7-4f23-a918-1d9d89086a68"}},"directories":{},"maintainers":[{"name":"alexleekt","email":"stratrix@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-patch-manager_0.4.0_1788672267098_0.35884000615304346"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-28T22:06:14.149Z","modified":"2026-09-06T05:24:27.495Z","0.3.0":"2026-08-28T22:06:14.506Z","0.4.0":"2026-09-06T05:24:27.225Z"},"bugs":{"url":"https://github.com/alexleekt/pi-extensions/issues"},"author":{"name":"Alex Lee","email":"657215+alexleekt@users.noreply.github.com"},"license":"MIT","homepage":"https://github.com/alexleekt/pi-extensions/tree/main/packages/pi-patch-manager#readme","keywords":["pi-package","pi-extension","pi","patch","patch-manager"],"repository":{"type":"git","url":"git+https://github.com/alexleekt/pi-extensions.git"},"description":"Safe inspection and management of Git patches for Pi packages.","maintainers":[{"name":"alexleekt","email":"stratrix@gmail.com"}],"readme":"# @alexleekt/pi-patch-manager\n\nInstall with `pi install npm:@alexleekt/pi-patch-manager`. The manager reads patches from `~/.pi/agent/patches/<id>/` and supports npm-style packages found under the agent, agent extensions, and current project `node_modules` roots.\n\nEach directory contains a strict `manifest.json`. Required fields: `id`, `package`, `baseVersion`, `baseHash` (`sha256:` plus 64 lowercase hex characters), `patch` (a relative path under `patch/`), `intent`, `reason`, and `enabled`. Optional fields capture the plan's full context: `target` (`file`, `symbol`, `change` — `file` is validated as a safe relative path), `validation` (safe relative path to a checks script), `upstream` (`status` kebab-case, `url` http(s)), and `createdWith` (`provider`, `model` provenance). Unknown fields and unsafe paths are rejected.\n\n`/patch list`, `/patch status`, and `/patch explain <id>` inspect the registry. `/patch disable <id>` atomically changes only `enabled` to false, writing through a random temp directory inside the patch directory to defeat symlink pre-planting. `/patch apply [id]` applies one or all enabled patches: the patch bytes are snapshotted to a private temp file, guarded by a dry-run (`git apply --check`), applied all-or-nothing (never `--reject` or `--3way`), then reverse-verified. Git refuses any patch path escaping the package root (no `--unsafe-paths`; git subprocesses run at the package root with inherited `GIT_*` environment stripped). Drifted packages are refused — a version change normally requires a rebase. If the old patch still reverse-applies after the version change, status is `possibly-unneeded`: the patch may no longer belong in the new package and `/patch status` offers Disable rather than Rebase. A failed `validation` script yields a distinct `validation-failed` outcome: the patch is present but unhealthy. Validation scripts are user-authored code that run with full user privileges; their output is only ever displayed, never fed to an agent. Apply-all is atomic per patch, not across the batch, and there is no cross-process lock. The `patch_status` agent tool is read-only.\n\nStatus is computed from package identity, deterministic SHA-256 hashes over a sorted file walk (symlinks rejected at every level, including the package root), and reverse `git apply --check` classification. A different package version is drift; it is classified as `possibly-unneeded` when the old patch still reverse-applies, or `drifted` when it does not. `applied` proves the patch itself is present, not that the whole tree matches a recorded post-patch state. `/patch status` groups entries as NEEDS REBASE, POSSIBLY NO LONGER NEEDED, FAILED OR MISSING, READY TO APPLY, HEALTHY, and DISABLED. Its action menu offers Rebase for drifted patches and Disable for possibly-unneeded patches. Patch files are resolved through `realpath` and must stay inside their patch directory. Git subprocesses run at the package root with `--no-index` and all inherited `GIT_*` environment stripped, so path resolution never depends on where pi was started. No reject or three-way modes are used.\n\n## Dependencies\n\nZero runtime dependencies. Peer dependencies declare the Pi integration surface: `@earendil-works/pi-coding-agent`, `@earendil-works/pi-ai` (used only by the optional `/patch rebase` LLM flow), and `typebox`.\n\n## Crafting patches\n\nThe package ships a `patch-creator` skill (available in pi as `/skill:patch-creator` after install). It walks through snapshotting the pristine package, editing it, generating a registry-compatible diff, and writing `manifest.json`. Its `create-patch.mjs` helper imports this package's own hashing so generated hashes always match the registry's algorithm, and self-verifies the generated patch (forward-apply to the pristine snapshot, reverse-apply to the edited snapshot) before reporting success. Crafted patches report `applied` in `/patch status` once the edit is present and identity matches. `/patch apply` applies them; `/patch rebase` rebuilds a drifted patch.\n\n## Rebase\n\n`/patch rebase <id>` handles drift: the old and new package versions and hashes, the original patch, the manifest's intent, reason, target, and validation are collected into a context, together with bounded source excerpts from the newly installed package around each hunk, and the first available model is asked for a JSON candidate patch. Source and patch text are sent to the model as untrusted data. Oversized input is rejected before any of it is read: the original patch must fit 64 KB, prompt fields and excerpts are capped individually, and the total prompt must fit a 100 KB byte budget before the model is called. The candidate is then validated without touching the installed package: shape checks (JSON `{patch}` only, size caps, `diff --git` present, no code fences), safe-path checks on every `---`/`+++` target, a dry-run forward apply plus reverse verification inside a private temp copy of the package (with the old patch first reverse-applied in the copy, and control patches kept outside the copied tree), and a `checks.sh` run there. The candidate diff and validation result are shown, and an explicit confirmation dialog gates every mutation.\n\nOn approval, the patch registry is re-read and compared (manifest bytes, patch bytes, resolved patch path) before any history write or package mutation; the package hash and identity are re-verified as well. The old patch and manifest are archived under `history/<timestamp>-<hash>/` preserving the manifest's own patch path; the old patch is reverse-applied if it was present; the candidate is applied to the live package; validation and a full-tree hash check run again; and only then does the rebase commit, by writing the candidate to a new immutable patch file and atomically renaming the manifest as the commit point — before the switch, readers see the old manifest and old patch; after it, the new manifest and completed candidate. The manifest records the new pristine base version and hash (with the old patch removed) plus `rebasedFrom` provenance (`model`, `date`, `previousBase`). A validation failure, package identity change, or patch-registry change mid-flight routes through rollback: the candidate is reversed, the old patch restored, and the restoration verified against the recorded pre-rebase tree hash — with primary and rollback errors both reported if restoration fails. The model is always the first available; there is no model choice yet, and an LLM-generated patch is never applied without explicit user approval.","readmeFilename":"README.md"}