{"id":49843339,"url":"https://github.com/davidcreador/pi-dcp","last_synced_at":"2026-05-14T08:03:37.200Z","repository":{"id":357505761,"uuid":"1237257560","full_name":"Davidcreador/pi-dcp","owner":"Davidcreador","description":"Cut LLM token spend in long Pi sessions, automatically. Dedup redundant tool calls, strip errored payloads, and let the model summarize closed work-streams — all without ever modifying your session history.","archived":false,"fork":false,"pushed_at":"2026-05-13T03:26:48.000Z","size":179,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-13T05:06:14.791Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Davidcreador.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-13T02:46:17.000Z","updated_at":"2026-05-13T03:26:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Davidcreador/pi-dcp","commit_stats":null,"previous_names":["davidcreador/pi-dcp"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/Davidcreador/pi-dcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Davidcreador%2Fpi-dcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Davidcreador%2Fpi-dcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Davidcreador%2Fpi-dcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Davidcreador%2Fpi-dcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Davidcreador","download_url":"https://codeload.github.com/Davidcreador/pi-dcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Davidcreador%2Fpi-dcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33015817,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"online","status_checked_at":"2026-05-14T02:00:06.663Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-14T08:03:33.534Z","updated_at":"2026-05-14T08:03:37.186Z","avatar_url":"https://github.com/Davidcreador.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pi-Dynamic-Context-Pruning\n\nCut LLM token spend in long [Pi](https://github.com/earendil-works/pi/tree/main/packages/coding-agent) sessions, automatically. Dedup redundant tool calls, strip errored payloads, and let the model summarize closed work-streams — all without ever modifying your session history.\n\nA faithful port of [@tarquinen/opencode-dcp](https://github.com/Opencode-DCP/opencode-dynamic-context-pruning) tailored to pi's extension API. Zero npm dependencies at runtime.\n\nLicense: AGPL-3.0-or-later. Tests: 55 passing on Node 22 and 24.\n\n## Contents\n\n- [Why](#why)\n- [How it works](#how-it-works)\n- [Install](#install)\n- [Quick start](#quick-start)\n- [Slash commands](#slash-commands)\n- [Configuration reference](#configuration-reference)\n- [Per-model context limits](#per-model-context-limits)\n- [Recipes](#recipes)\n- [Troubleshooting](#troubleshooting)\n- [Develop](#develop)\n- [How it differs from opencode-dcp](#how-it-differs-from-opencode-dcp)\n- [Project layout](#project-layout)\n- [Credits and license](#credits-and-license)\n\n## Why\n\nA long agentic loop in Pi typically wastes tokens on:\n\n- Repeated lookups — the same `grep` or `read` re-issued five turns later\n- Failed payloads — a 4-KB `bash` command that errored, sent back to the model on every subsequent turn\n- Closed work-streams — initial repo scans, abandoned approaches, resolved retry loops whose raw output is no longer useful\n\npi-dcp prunes all three before the request hits the model. The on-disk session is never touched — pruning is applied to the request payload only, so `/tree`, `/compact`, fork, and resume all keep the originals intact.\n\n## How it works\n\nA `context` event fires on every outbound LLM request. pi-dcp hooks that event, rewrites the request payload in-place, and lets it continue to the provider. The on-disk session is never mutated.\n\nThree independent mechanisms run on the outbound request:\n\n| Mechanism | What it does | When |\n|---|---|---|\n| Deduplication | Same `toolName + canonical(args)` keeps the newest result and replaces older copies with a `[pruned by pi-dcp: duplicate ... call]` marker. | Every LLM call (auto) |\n| Errored input purge | Failed tool calls have their arguments stripped after N turns. Error message is preserved. | Every LLM call (auto) |\n| `compress` tool | LLM-callable. Replaces a span of tool results with a lossless technical summary. Two modes: `message` (per-id list) or `range` (start and end span). | When the model decides |\n\nPlus three nudge surfaces that bias the model toward compressing:\n\n- Soft or strong in-system-prompt nudge when usage crosses `minContextLimit`\n- Hard nudge above `maxContextLimit`\n- Iteration nudge after N non-user messages without a user reply\n\n## Install\n\nThree ways. Pick one:\n\nnpm (recommended — versioned, easy to update):\n\n```bash\npi install npm:@davecodes/pi-dcp\n```\n\ngit (always tracks `main`):\n\n```bash\npi install git:github.com/Davidcreador/pi-dcp\n```\n\nmanual clone (for hacking on the code):\n\n```bash\ngit clone git@github.com:Davidcreador/pi-dcp.git ~/.pi/agent/extensions/pi-dcp\n```\n\nAll three paths produce the same runtime behavior. Pi auto-discovers the extension via its `pi.extensions` package.json entry.\n\nUser state always lives at `~/.pi-dcp/`, not next to the code. That directory holds:\n\n```\n~/.pi-dcp/\n  config.json              your settings (written on first run from defaults)\n  prompts/\n    defaults/              regenerated each launch (read-only reference)\n    overrides/             drop *.md files here to customize the LLM prompts\n  dcp.log                  debug log (when config.debug is true)\n  stats.json               lifetime savings counters\n```\n\nTo update:\n\n```bash\npi update npm:@davecodes/pi-dcp\n```\n\n## Quick start\n\nAfter install, run pi normally. Verify the extension is live:\n\n```bash\npi -p \"do you have a tool called 'compress'? answer yes/no\"\n```\n\nExpected output: `yes`.\n\nOpen a long session as usual. Check savings any time with:\n\n```\n/dcp context     # this session\n/dcp stats       # lifetime, across sessions\n```\n\nTo bias the model toward compressing more aggressively, edit `~/.pi-dcp/config.json`:\n\n```json\n{\n  \"compress\": {\n    \"minContextLimit\": \"30%\",\n    \"maxContextLimit\": \"60%\",\n    \"nudgeForce\": \"strong\"\n  }\n}\n```\n\n`minContextLimit` is the soft-nudge floor. `maxContextLimit` is the hard-nudge ceiling. Restart pi after config changes.\n\n## Slash commands\n\n| Command | What it does |\n|---|---|\n| `/dcp` | Show this command list |\n| `/dcp context` | Current session: token usage, DCP savings, active compressions |\n| `/dcp stats` | Lifetime savings across all pi sessions |\n| `/dcp sweep [n]` | Stage a compression over the last n tool results (default: since last user message). Use to nuke unwanted output. |\n| `/dcp manual on/off/toggle/status` | Runtime manual mode — stops the LLM from auto-compressing. Edit `config.json` to persist. |\n| `/dcp decompress \u003cid\u003e` | Temporarily restore a stored compression's original tool outputs |\n| `/dcp recompress \u003cid\u003e` | Re-apply a previously decompressed entry |\n\nSlash commands work in interactive pi mode only — `pi -p` (print mode) does not dispatch them. The compress tool and auto strategies work in both modes.\n\n## Configuration reference\n\nDefaults are written to `~/.pi-dcp/config.json` on first run. Per-project overrides at `\u003crepo\u003e/.pi/dcp.json` shallow-merge on top. Restart pi after edits.\n\nThe shipped defaults are tuned for real-world long sessions — see `config.example.json` in this repo for the exact reference shape and inline comments.\n\n```json\n{\n  \"enabled\": true,\n  \"debug\": false,\n  \"pruneNotification\": \"minimal\",\n\n  \"experimental\": {\n    \"customPrompts\": false\n  },\n\n  \"manualMode\": {\n    \"enabled\": false,\n    \"automaticStrategies\": true\n  },\n\n  \"turnProtection\": {\n    \"enabled\": true,\n    \"turns\": 3\n  },\n\n  \"compress\": {\n    \"mode\": \"range\",\n    \"permission\": \"allow\",\n    \"minContextLimit\": 30000,\n    \"maxContextLimit\": 70000,\n    \"modelMinLimits\": {\n      \"anthropic/claude-opus-4-7\": 35000\n    },\n    \"modelMaxLimits\": {\n      \"anthropic/claude-opus-4-7\": 85000\n    },\n    \"nudgeEveryTurns\": 5,\n    \"nudgeFrequency\": 3,\n    \"iterationNudgeThreshold\": 8,\n    \"nudgeForce\": \"strong\",\n    \"protectedTools\": []\n  },\n\n  \"strategies\": {\n    \"deduplication\": {\n      \"enabled\": true,\n      \"protectedTools\": []\n    },\n    \"purgeErrors\": {\n      \"enabled\": true,\n      \"turns\": 2,\n      \"protectedTools\": []\n    }\n  }\n}\n```\n\nField notes:\n\n- `pruneNotification`: `off`, `minimal`, or `detailed` (reserved).\n- `experimental.customPrompts`: when `true`, honors `prompts/overrides/*.md`.\n- `manualMode.automaticStrategies`: when manual mode is on, still run dedup and purge.\n- `turnProtection.turns`: last N user-bounded turns are immune to pruning.\n- `compress.minContextLimit` / `maxContextLimit`: number of tokens or a `\"X%\"` string of the model's context window.\n- `compress.nudgeEveryTurns`: per-turn soft-nudge throttle.\n- `compress.nudgeFrequency`: per-request soft-nudge throttle (stacks with the per-turn one).\n- `compress.iterationNudgeThreshold`: 0 disables; fires after N messages since the last user message.\n- `compress.nudgeForce`: `soft` or `strong` wording.\n- `strategies.purgeErrors.turns`: turns after which errored args are purged.\n\nAlways protected (never pruned, regardless of config): `compress`, `write`, `edit`, `todo`, `task`, `skill`.\n\n## Per-model context limits\n\n`compress.modelMinLimits` and `modelMaxLimits` accept keys shaped as `provider/id`, matching `ctx.model.provider` and `ctx.model.id`. Examples mirroring the shipped `config.json`:\n\n| Model | Window | Soft floor | Hard ceiling | Strategy |\n|---|---|---|---|---|\n| `anthropic/claude-haiku-4-5` | 200k | 30k | 70k | tight — cheap fast tier |\n| `anthropic/claude-sonnet-4-5` | 200k | 50k | 120k | workhorse band |\n| `anthropic/claude-sonnet-4-6` | 200k | 50k | 120k | workhorse band |\n| `anthropic/claude-opus-4-1` to `4-7` | 200k | 35k | 85k | aggressive — save expensive tokens |\n| `openai/gpt-5.4-mini-fast` | — | 25k | 50k | tightest |\n| `openai/gpt-5.4-mini` | — | 30k | 70k | tight |\n| `openai/gpt-5.5` | — | 45k | 100k | medium |\n\nValues accept either a number (absolute token count) or a `\"X%\"` string (percentage of the model's context window).\n\n## Recipes\n\n### Save tokens aggressively on premium models\n\n```json\n{\n  \"compress\": {\n    \"modelMinLimits\": { \"anthropic/claude-opus-4-7\": \"10%\" },\n    \"modelMaxLimits\": { \"anthropic/claude-opus-4-7\": \"25%\" },\n    \"nudgeForce\": \"strong\"\n  }\n}\n```\n\n### Do not auto-compress, let me drive\n\n```json\n{\n  \"manualMode\": { \"enabled\": true, \"automaticStrategies\": true }\n}\n```\n\nAuto-dedup and purge still run. You drive compression via `/dcp sweep`.\n\n### Project-specific overrides\n\nDrop a `.pi/dcp.json` in the repo root:\n\n```json\n{\n  \"strategies\": {\n    \"purgeErrors\": {\n      \"turns\": 1,\n      \"protectedTools\": [\"lint\"]\n    }\n  }\n}\n```\n\n### Customize the nudge wording\n\n```json\n{\n  \"experimental\": { \"customPrompts\": true }\n}\n```\n\nThen create `~/.pi-dcp/prompts/overrides/strong-nudge.md` with your text. Restart pi.\n\n## Troubleshooting\n\nThe compress tool is not showing up:\n\n- Confirm with `pi -p \"list your tools\" 2\u003e\u00261 | grep compress`. If missing, check `compress.permission` is not `\"deny\"`.\n- Restart pi after any config change. Extensions load once at startup.\n\nNothing is being pruned:\n\n- `/dcp context` shows live stats. If always 0:\n  - `turnProtection.turns` may cover your whole session (recent turns are protected).\n  - `strategies.*.enabled` may be `false`.\n  - You may be hitting protected tools — `write` and `edit` are never deduped.\n\nSee what is happening under the hood:\n\n```json\n{ \"debug\": true }\n```\n\nRestart pi. Logs land at `~/.pi-dcp/dcp.log`:\n\n```\n[2026-05-13T...] INFO pi-dcp initialized {\"mode\":\"range\",...}\n[2026-05-13T...] INFO pipeline applied {\"dedupPruned\":2,\"errorInputsPurged\":1,\"tokensSaved\":3214}\n```\n\nCompress tool refuses with `protected_window_overlap`:\n\n- The model picked tool-call IDs that live inside `turnProtection.turns`. Either lower `turnProtection.turns`, disable it, or tell the model to pick older calls.\n\n## Develop\n\n```bash\ncd ~/.pi/agent/extensions/pi-dcp\n\n# Set up dev deps (peer + typescript)\nnpm install --no-save typescript @earendil-works/pi-coding-agent\n\n# Typecheck + test\nnpm run check\nnpm run test\n```\n\nCI on GitHub Actions runs the same on Node 22 and 24 against every push and PR.\n\n## How it differs from opencode-dcp\n\n| Feature | opencode-dcp | pi-dcp |\n|---|---|---|\n| Tokenizer | `@anthropic-ai/tokenizer` | `ctx.getContextUsage()` (built-in) |\n| Auto-update | npm latest check | `git pull` |\n| Soft/hard nudges | per-request injection | `before_agent_start` system-prompt addendum (functionally equivalent) |\n| `compress.mode` (range, message) | both | both |\n| `turnProtection` | runtime skip | runtime skip plus upfront refusal of compress tool overlap |\n| `modelMinLimits` and `modelMaxLimits` | yes | yes |\n| `iterationNudgeThreshold` | yes | yes |\n| `nudgeForce` | yes | yes |\n| `compress.nudgeFrequency` | yes | yes, plus per-turn `nudgeEveryTurns` |\n| Prompt overrides | yes | yes |\n| `manualMode.automaticStrategies` | yes | yes |\n| Skipped in pi-dcp | — | `pruneNotificationType:\"toast\"`, `compress.showCompression`, `compress.summaryBuffer`, `experimental.allowSubAgents`, `protectedFilePatterns` |\n\n## Project layout\n\n```\npi-dcp/\n  index.ts                          extension entry — wires hooks, tool, /dcp command\n  config.json                       runtime config (auto-generated; tracked)\n  lib/\n    config.ts                       loader, DEFAULT_CONFIG, percent + per-model resolution\n    logger.ts                       ~/.pi-dcp/dcp.log writer (gated by config.debug)\n    state.ts                        per-session in-memory state\n    stats.ts                        ~/.pi-dcp/stats.json lifetime counters (atomic write)\n    messages.ts                     AgentMessage helpers + canonical JSON + cloneForMutation\n    pipeline.ts                     orchestrates strategies + applies compressions\n    nudges.ts                       soft/strong/hard/iteration system-prompt addendums\n    strategies/\n      deduplication.ts              drop redundant tool calls\n      purge-errors.ts               strip errored tool inputs after N turns\n    tools/\n      compress-message.ts           LLM tool — per-id mode\n      compress-range.ts             LLM tool — span mode\n      shared.ts                     preflight, storeCompression, branchToolCallIds\n    prompts/\n      index.ts                      PromptStore + defaults + override loader\n    commands/                       /dcp subcommand handlers\n      help.ts\n      context.ts\n      stats.ts\n      manual.ts\n      sweep.ts\n      decompress.ts                 decompress + recompress\n  test/                             55 unit tests, zero external deps\n    pipeline.test.ts                dedup, purge, mutation safety, idempotency\n    misc.test.ts                    config percent parsing, nudge throttling, parseStrictId\n    features.test.ts                range mode, prompt overrides, manual modes, nudgeFreq\n    parity.test.ts                  turnProtection, modelMin/Max, iterationNudge, nudgeForce\n    audit.test.ts                   edge cases (no-user, iter-refire, protected overlap)\n  skills/pi-dcp/SKILL.md            documentation surface pi reads at session start\n  prompts/defaults/                 regenerated on every init (read-only reference)\n  prompts/overrides/                you put files here when customPrompts is true\n  .github/workflows/ci.yml          Node 22/24 matrix typecheck + test\n  README.md\n```\n\n## Credits and license\n\nConcept and prompt design ported from [@tarquinen/opencode-dcp](https://github.com/Opencode-DCP/opencode-dynamic-context-pruning) by tarquinen. Pi adaptation and tests by [@Davidcreador](https://github.com/Davidcreador).\n\nLicense: AGPL-3.0-or-later — inherits from upstream. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidcreador%2Fpi-dcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidcreador%2Fpi-dcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidcreador%2Fpi-dcp/lists"}