{"id":50282475,"url":"https://github.com/rahadiana/opencode-ultrapress","last_synced_at":"2026-05-28T01:00:19.108Z","repository":{"id":357231020,"uuid":"1236013285","full_name":"rahadiana/opencode-ultrapress","owner":"rahadiana","description":"UltraPress saves context window tokens through 4 compression layers that run automatically in the background — from CLI output filtering, semantic compression, dynamic context pruning, to auto-cleanup. Your LLM stays smart, tokens stay lean.","archived":false,"fork":false,"pushed_at":"2026-05-22T17:38:31.000Z","size":321,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T21:32:59.980Z","etag":null,"topics":["opencode","opencode-plugin"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rahadiana.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-11T21:44:49.000Z","updated_at":"2026-05-22T17:38:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"0a2556e1-15c5-4660-9fb5-057fadaa7d56","html_url":"https://github.com/rahadiana/opencode-ultrapress","commit_stats":null,"previous_names":["rahadiana/opencode-ultrapress"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rahadiana/opencode-ultrapress","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahadiana%2Fopencode-ultrapress","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahadiana%2Fopencode-ultrapress/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahadiana%2Fopencode-ultrapress/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahadiana%2Fopencode-ultrapress/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rahadiana","download_url":"https://codeload.github.com/rahadiana/opencode-ultrapress/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rahadiana%2Fopencode-ultrapress/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33589684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"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":["opencode","opencode-plugin"],"created_at":"2026-05-28T01:00:15.650Z","updated_at":"2026-05-28T01:00:19.068Z","avatar_url":"https://github.com/rahadiana.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/image/banner.svg\" alt=\"UltraPress Banner\" width=\"100%\" /\u003e\n\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n# 🚀 OpenCode UltraPress\n\n**Token Compression Plugin for OpenCode AI**\n\n[![CI](https://github.com/rahadiana/opencode-ultrapress/actions/workflows/ci.yml/badge.svg)](https://github.com/rahadiana/opencode-ultrapress/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/@rahadiana/opencode-ultrapress?color=red)](https://www.npmjs.com/package/@rahadiana/opencode-ultrapress)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n\u003c/div\u003e\n\n\u003e **UltraPress** saves context window tokens through 4 compression layers that run automatically in the background — from CLI output filtering, semantic compression, dynamic context pruning, to auto-cleanup. Your LLM stays smart, tokens stay lean.\n\n---\n\n## 📑 Table of Contents\n\n- [⚡ Installation \u0026 Setup](#-installation--setup)\n  - [System Requirements](#system-requirements)\n  - [Install from GitHub](#1-install-the-plugin)\n  - [Register to OpenCode](#2-register-to-opencode)\n  - [Personal Configuration](#3-optional-create-personal-configuration)\n  - [Verify Installation](#verify-installation)\n  - [Uninstall](#uninstall)\n- [🛠 4-Layer Architecture](#-4-layer-architecture)\n  - [Pipeline Flow](#pipeline-flow)\n  - [Layer 1 — Smart Output Filter](#layer-1--smart-output-filter)\n  - [Layer 2 — GSC Semantic Compression](#layer-2--gsc-semantic-compression)\n  - [Layer 3 — Dynamic Context Pruning (DCP)](#layer-3--dynamic-context-pruning-dcp)\n  - [Layer 4 — Session Auto-Cleanup](#layer-4--session-auto-cleanup)\n- [⚙️ Configuration](#️-configuration)\n  - [Full documentation →](./docs/konfigurasi-lengkap.md)\n- [⌨️ `/up` Slash Command](#️-up-slash-command)\n  - [Sub-command List](#sub-command-list)\n  - [Example Output](#example-output)\n- [❷ MLM \u0026 NLP Support](#-mlm--nlp-support)\n  - [NLP Mode (Default)](#nlp-mode-default)\n  - [MLM Mode (Experimental)](#mlm-mode-experimental)\n  - [Mode Comparison](#mode-comparison)\n- [🏗 Code Architecture](#-code-architecture)\n  - [Directory Structure](#directory-structure)\n  - [Hook Registration Map](#hook-registration-map)\n  - [Data Flow Detail](#data-flow-detail)\n- [🧪 Testing](#-testing)\n- [📊 Benchmark](#-benchmark)\n- [🚀 Local Development](#-local-development)\n- [❓ FAQ \u0026 Troubleshooting](#-faq--troubleshooting)\n- [🗺 Roadmap](#-roadmap)\n- [🤝 Contributing](#-contributing)\n- [📝 Changelog](#-changelog)\n- [📄 License](#-license)\n\n---\n\n## ⚡ Installation \u0026 Setup\n\n### System Requirements\n\n| Dependency | Minimum Version | Notes |\n| :--- | :--- | :--- |\n| **Node.js** | `\u003e= 18` | Node 22 LTS recommended |\n| **OpenCode AI** | Latest | Uses `@opencode-ai/plugin ^1.14` |\n| **Git** | Any | Required for GitHub install |\n| **Bun** | Latest | For development/testing only |\n| **@huggingface/transformers** | Auto-install | Only used when `mlm` mode is active |\n\n### Compatibility Matrix (Runtime)\n\n| Environment | NLP (default) | MLM | LLM | Notes |\n| :--- | :---: | :---: | :---: | :--- |\n| macOS (Intel/Apple Silicon) | ✅ | ✅ | ✅ | Best overall support |\n| Linux | ✅ | ✅ | ✅ | Good for server/workstation |\n| WSL2 | ✅ | ✅ | ✅ | Prefer enough RAM for MLM/LLM |\n| Windows | ✅ | ✅ | ✅ | Works via Node runtime |\n| Termux / low-resource mobile shell | ✅ | ⚠️ | ⚠️ | Prefer NLP mode to avoid model load overhead |\n\nLegend: ✅ recommended · ⚠️ possible but resource-sensitive\n\n### Cross-Platform Notes\n\nUltraPress is pure TypeScript and works across all platforms OpenCode supports:\n\n- macOS\n- Linux\n- WSL / Termux\n- Windows\n\nWhat to expect:\n\n- `mlm` / `llm` modes may download model assets on first use (larger disk/RAM footprint).\n- If your environment is resource-limited (e.g., small VPS/Termux), use default **Balanced (NLP)** or force `semantic.mode: \"nlp\"` for zero-model operation.\n\n### 1. Install the Plugin\n\n```bash\n# ✅ RECOMMENDED — auto-registers with OpenCode\nopencode plugin add @rahadiana/opencode-ultrapress@latest --global\n```\n\n\u003e ⚠️ **Important:** OpenCode caches the plugin at `~/.cache/opencode/packages/`. `@latest` is resolved **once** on first install — subsequent versions won't auto-update. To upgrade:\n\u003e\n\u003e ```bash\n\u003e rm -rf ~/.cache/opencode/packages/@rahadiana/opencode-ultrapress@latest\n\u003e opencode plugin add @rahadiana/opencode-ultrapress@latest --global\n\u003e ```\n\u003e\n\u003e The plugin will warn you at startup if a newer version is available.\n\n**Alternative install methods** (not recommended for end users):\n\n```bash\n# Via npm — requires manual registration (step 2), same cache caveat applies\nnpm install -g @rahadiana/opencode-ultrapress\n\n# GitHub latest — for testing pre-release changes\nnpm install -g github:rahadiana/opencode-ultrapress\n```\n\n**For plugin development:**\n\n```bash\ngit clone https://github.com/rahadiana/opencode-ultrapress.git\ncd opencode-ultrapress\nnpm install\nnpm run build\n\n# Link globally so OpenCode can find it\nnpm link\n\n# Then in OpenCode's config (~/.config/opencode/config.json), add:\n# { \"plugins\": [\"@rahadiana/opencode-ultrapress\"] }\n#\n# After making code changes, re-run:\n# npm run build\n#\n# No need to re-link — OpenCode loads from the linked directory.\n# If using opencode plugin add, remove the cached version first:\n# rm -rf ~/.cache/opencode/packages/@rahadiana/opencode-ultrapress@latest\n```\n\n### 2. Register to OpenCode (npm install only)\n\nIf you installed via `opencode plugin`, skip this step — registration is automatic.\n\nAdd the plugin to OpenCode's config at `~/.config/opencode/config.json`:\n\n```json\n{\n  \"plugins\": [\"@rahadiana/opencode-ultrapress\"]\n}\n```\n\n### 3. (Optional) Create Personal Configuration\n\nUltraPress works out-of-the-box with **Balanced defaults**. For customization:\n\n```bash\n# Install from GitHub → copy from the cloned repo\ncp ultrapress.plugin.jsonc.example ~/.config/opencode/ultrapress.plugin.json\n\n# Or from global install\ncp $(npm root -g)/@rahadiana/opencode-ultrapress/ultrapress.plugin.json.example ~/.config/opencode/ultrapress.plugin.json\n```\n\nThen edit `~/.config/opencode/ultrapress.plugin.json` as needed. If the file is not found, UltraPress will automatically create it with default values on first run.\n\n### Quick-Start Profiles by Device\n\nUse this as a practical starting point:\n\n| Device / Environment | Recommended Mode | Why |\n| :--- | :--- | :--- |\n| Low-RAM machine / Termux / tiny VPS | `semantic.mode: \"nlp\"` | Zero model download, lowest RAM/CPU overhead |\n| Typical laptop/dev machine | **Balanced defaults** | Best trade-off: token savings + context safety |\n| High-RAM workstation | `mlm` or `llm` (optional) | Higher semantic quality, more resource usage |\n\nMinimal override examples:\n\n```jsonc\n// Low-resource profile\n{\n  \"semantic\": { \"mode\": \"nlp\" }\n}\n```\n\n```jsonc\n// Higher-quality semantic profile (requires more RAM)\n{\n  \"semantic\": {\n    \"mode\": \"mlm\",\n    \"model\": \"Xenova/all-MiniLM-L6-v2\"\n  }\n}\n```\n\n### Verify Installation\n\nRestart OpenCode, then type in chat:\n\n```\n/up stats\n```\n\nIf a statistics dashboard appears, UltraPress is active. If not:\n1. Installed via `npm install`? Make sure the plugin is in `config.json` → `\"plugins\": [\"@rahadiana/opencode-ultrapress\"]`\n2. Installed via `opencode plugin`? Run `opencode plugin list` to confirm it's registered.\n2. Check OpenCode logs for errors\n3. Ensure Node.js \u003e= 18 is installed (`node --version`)\n\n### Uninstall\n\n```bash\n# 1. Remove from OpenCode plugin list\n#    Edit ~/.config/opencode/config.json — remove \"@rahadiana/opencode-ultrapress\" from plugins array\n\n# 2. Purge the cached version\nrm -rf ~/.cache/opencode/packages/@rahadiana/opencode-ultrapress@latest\n\n# 3. If installed via npm (global)\nnpm uninstall -g @rahadiana/opencode-ultrapress\n\n# 4. If using npm link\nnpm unlink -g @rahadiana/opencode-ultrapress\n\n# 5. Clean up config\nrm ~/.config/opencode/ultrapress.plugin.json\n```\n\n---\n\n## 🛠 4-Layer Architecture\n\nUltraPress intercepts OpenCode message flow at 4 different points, each with a specific compression strategy.\n\n### Pipeline Flow\n\n```mermaid\nflowchart LR\n    A([Tool Output]) --\u003e|\"tool.execute.after\"| L1[Layer 1\\nOutput Filter]\n    B([Chat Message]) --\u003e|\"chat.message\"| PRUNE{Prune\\nPending?}\n    PRUNE --\u003e|yes| REMOVE[Remove msgs\\n\u0026 inject summary]\n    PRUNE --\u003e|no| L2[Layer 2\\nGSC Semantic]\n    REMOVE --\u003e L2\n    L2 --\u003e L3[Layer 3\\nNudge Monitor]\n    L3 --\u003e|\"nudge injected\"| LLM{LLM}\n    LLM --\u003e|\"calls tool\"| DCP_TOOL[ultrapress_compress]\n    DCP_TOOL -.-\u003e|\"block stored\"| PRUNE\n    D([Session Compact]) --\u003e|\"session.compacting\"| L4[Layer 4\\nCleanup]\n\n    L1 \u0026 L2 \u0026 L4 --\u003e CTX[(Context\\nWindow)]\n```\n\n---\n\n### Layer 1 — Smart Output Filter\n\n\u003e **Hook**: `tool.execute.after` · **File**: `layer1-output-filter.ts` · **Filters**: `src/filters/`\n\nIntercepts CLI tool output **before** it enters the context window. The most aggressive layer — directly cuts unnecessary logs.\n\n**Core Strategies:**\n\n| Strategy | Description |\n| :--- | :--- |\n| **Domain Routing** | Each CLI tool is routed to a specific filter: `git`, `npm/node`, `pytest/jest`, and filesystem. Unknown tools go to the generic filter. |\n| **Middle-out Truncation** | Truncates logs from the middle, preserving the beginning (context) and end (error/result). Smarter than head/tail truncation. |\n| **Deduplication** | Removes identical repeated log lines in real-time. Very effective for build \u0026 test logs. |\n| **Tee Save** | If output is truncated, the original log is saved to a temporary `.log` file so it can still be accessed if needed. |\n\n**Built-in Filters:**\n\n| Filter | File | Trigger Tools |\n| :--- | :--- | :--- |\n| **Git** | `filters/git.ts` | `git diff`, `git log`, `git show` — remove redundant diff hunks, keep summary |\n| **Test** | `filters/test.ts` | `pytest`, `jest`, `vitest`, `mocha` — summarize failure output, remove passing tests |\n| **Bash** | `filters/bash.ts` | Generic shell output — dedup lines, middle-out truncation |\n| **Filesystem** | `filters/fs.ts` | `ls`, `cat`, `find` — limit file count, truncate long content |\n| **Generic** | `filters/generic.ts` | Fallback for all other tools — middle-out truncation + dedup |\n\n---\n\n### Layer 2 — GSC Semantic Compression\n\n\u003e **Hook**: `chat.message` · **File**: `layer2-caveman.ts` · **Engine**: `src/caveman/`\n\nCompresses message text **semantically** — removes unimportant words without changing meaning. Layer 2 and Layer 3 **do not compress each other** — no double compression.\n\n**Compression Rules:**\n\n- Conjunctions (`that`, `and`, `will`, `which`) → removed\n- Excessive pronouns → condensed\n- Redundancy (\"I think I will\" → \"I will\") → removed\n- Double spaces, unnecessary whitespace → normalized\n- **Code blocks** (inside ` ``` `) → **NEVER** touched\n- **Error messages \u0026 stack traces** → fully protected\n- Messages \u003c 200 characters → skipped\n\n**Operating Modes:**\n\nSee [❷ MLM \u0026 NLP Support](#-mlm--nlp-support) for detailed NLP vs MLM comparison.\n\n---\n\n### Layer 3 — Dynamic Context Pruning (DCP)\n\n\u003e **Hook**: `chat.message` (pruning) + `tool.execute.after` (compress tool) · **File**: `layer3-dcp.ts` · **Engine**: `src/dcp/`\n\nThe most advanced system: **gives LLM autonomy to manage its own memory**. Unlike Layer 2 which only compresses text, Layer 3 **actually removes old messages from the context window** and replaces them with summaries.\n\n**Mechanism:**\n\n```\n1. Context Monitor → detect tokens approaching maxContextLimit\n2. Autonomous Nudge → inject prompt into user message: \"context window nearly full, call ultrapress_compress\"\n3. LLM calls → ultrapress_compress(mode=\"range\", from=\u003cid\u003e, to=\u003cid\u003e)\n4. Compression Block → stored in memory (compress-state.ts)\n5. chat.message hook → check pending blocks → remove messages in range → inject summary as synthetic message\n```\n\n**Key Features:**\n\n| Feature | Description |\n| :--- | :--- |\n| **Block-based Pruning** | When `ultrapress_compress` is called, LLM determines the message range to summarize. Block is stored, then executed on the **next** chat (not current — avoids race condition). |\n| **prune via `chat.message` Hook** | Each new message → plugin checks pending blocks → removes messages in range from context array → injects summary. |\n| **Protected Content** | Critical tool output (`task`, `skill`, `todowrite`, `todoread`, `write`, `edit`, `ultrapress_compress`) is protected from pruning. |\n| **Marker Protection** | Messages containing `TODO`, `FIXME`, `HACK`, `ACTION ITEM`, `ROOT CAUSE`, `RCA`, `DECISION`, `BLOCKER` are preserved from pruning. |\n| **Nesting Support** | Compression can be done on top of previous compression. Nested summaries are auto-merged. |\n| **`preserveLastN`** | Protects the **last** N messages from pruning — keeps recent conversation context intact. Default: `4`. Set to `0` to disable. |\n| **Multi-Signal Scoring** | In addition to `preserveLastN`, each message is scored from 5 signals (recency, role, tool type, keyword, content size). High-scoring messages are preserved even in old blocks. Default: `0.45` (balanced). |\n| **Reversible Compression** | `ultrapress_expand` tool — LLM can \"expand\" previously summarized blocks to see the original content. Original content is stored in plugin memory (not in LLM context). |\n| **Nudge @70%** | Nudge is sent when context reaches 70% limit (not 100%), giving LLM time to compress before context is truly full. |\n| **summaryBuffer** | After pruning, provides breathing room (no immediate re-nudge). |\n\n**Two Pruning Modes:**\n\n| Mode | Description |\n| :--- | :--- |\n| `range` | Range-based compression: choose from_id and to_id. All messages in between are summarized into one. |\n| `message` | Surgical compression: choose one or more specific message IDs to summarize. |\n\n---\n\n### Layer 4 — Session Auto-Cleanup\n\n\u003e **Hook**: `tool.execute.after` + `session.compacting` · **File**: `layer4-cleanup.ts`\n\nAutomatically cleans \"garbage\" from the context window.\n\n**Features:**\n\n| Feature | Description |\n| :--- | :--- |\n| **Error Purging** | Removes error/failed tool messages after N chat turns (default: 4 turns). Stale errors only waste tokens. |\n| **Tool-Call Dedup** | Prevents LLM from repeating identical tool calls (same tool + args) in the same session. |\n\n---\n\n## ⚙️ Configuration\n\n\u003e 📖 **Full configuration documentation** (all keys, types, defaults, examples, presets, custom filters, troubleshooting) at:\n\u003e [`docs/konfigurasi-lengkap.md`](./docs/konfigurasi-lengkap.md)\n\n### Basic Structure\n\nFile: `~/.config/opencode/ultrapress.plugin.json`\n\n```jsonc\n{\n  \"enabled\": true,           // Master switch\n  \"notification\": \"minimal\", // \"off\" | \"minimal\" | \"detailed\"\n\n      \"outputFilter\": {},\n      \"semantic\": {},\n      \"summarization\": {},\n      \"cleanup\": {}\n}\n```\n\n| Layer | Key | Function |\n| :--- | :--- | :--- |\n| **L1** | `outputFilter` | Limit CLI output length, filter repetitive lines |\n| **L2** | `semantic` | NLP/MLM text compression without destroying meaning |\n| **L3** | `summarization` | Remove old messages, replace with summary, `preserveLastN` protection |\n| **L4** | `cleanup` | Dedup tool calls, auto-purge stale errors |\n\n\u003e 🔒 **Safety guard**: `task` is always enforced in `outputFilter.skipTools` and `semantic.skipTools` even if removed in user config. This prevents accidental sub-agent context loss.\n\n\u003e 👉 **[Open full documentation →](./docs/konfigurasi-lengkap.md)** covers all keys, types, defaults, custom filters, presets (Balanced / Aggressive / Conservative / NLP-only), and troubleshooting.\n\n---\n\n## ⌨️ `/up` Slash Command\n\nAll interactions with UltraPress via a single command: `/up`.\n\n### Sub-command List\n\n| Command | Alias | Description |\n| :--- | :--- | :--- |\n| `/up stats` | `s`, `stat` | Current session token savings dashboard |\n| `/up context` | `c`, `ctx` | Context window status: capacity, limit, remaining |\n| `/up compress` | `comp` | Show layer status + compression guide |\n| `/up help` | `h`, `?` | Command help |\n\n**Note**: Sub-commands are case-insensitive and support partial fuzzy matching.\n\n### Example Output\n\n**`/up stats`**:\n\n```\n📊 ULTRAPRESS STATS\n──────────────────────────────────────────\n  Raw tokens       : 127,450\n  Compressed tokens: 89,215\n  Tokens saved     : 38,235 (30.0%)\n\n  By Layer:\n  L1 Output Filter : 18,400\n  L2 Semantic      : 12,100\n  L3 Summarization :  5,835\n  L4 Cleanup       :  1,900\n\n  Activity:\n  Compressions : 3\n  Deduplications: 12\n  Errors purged: 2\n\n  Session: 2h 15m\n──────────────────────────────────────────\n```\n\n**`/up context`**:\n\n```\n🧠 CONTEXT STATUS\n──────────────────────────────────────────\n  Current tokens     : ~52,000\n  Max context limit  : 70,000\n  Available          : ~18,000 (25.7%)\n  Nudge threshold    : 40,000\n  Status             : 🟡 Nearing limit (nudge will fire soon)\n  Next nudge in      : 3 turns\n──────────────────────────────────────────\n```\n\n---\n\n## ❷ MLM \u0026 NLP Support\n\n### NLP Mode (Default)\n\nRule-based grammar stripping using linguistic rules. **Zero latency**, no external model required.\n\n**How it works:**\n1. Detect sentence structure (subject, predicate, object)\n2. Remove conjunctions, excessive pronouns, filler words\n3. Condense redundancy without changing meaning\n4. Protect code blocks \u0026 error messages\n\n### MLM Mode (Experimental)\n\nUses **Masked Language Model** via `@huggingface/transformers` (Transformers.js) for more accurate tokenization.\n\n**Activation:**\n\n```json\n{\n  \"semantic\": {\n    \"mode\": \"mlm\",\n    \"model\": \"Xenova/distilbert-base-uncased\"\n  }\n}\n```\n\n**Important Notes:**\n- ⚠️ Model auto-downloaded on first run (~70MB for distilbert-base)\n- ⚠️ First-run latency 5-15 seconds for model loading\n- ⚠️ RAM usage increases ~200MB when model is active\n- ⚠️ Compatibility: CPU-only (no GPU required)\n- 🌐 For Indonesian: use `Xenova/bert-base-multilingual-uncased`\n\n### Mode Comparison\n\n| Aspect | NLP | MLM | LLM |\n| :--- | :--- | :--- | :--- |\n| **Latency** | \u003c 1ms | 50-200ms | 1-5s |\n| **RAM** | 0 MB | ~70 MB (q8) | ~300 MB (q8) |\n| **Accuracy** | ~85% | ~95% | ~99% |\n| **Language** | Indonesian + English | 100+ languages | All |\n| **Internet Connection** | ❌ Not needed | ❌ Initial download only | ❌ Initial download only |\n| **Stable** | ✅ | ⚠️ Experimental | ⚠️ Experimental |\n| **Model** | — | `all-MiniLM-L6-v2` | `t5-small` (summarization) |\n\n---\n\n## 🏗 Code Architecture\n\n### Directory Structure\n\n```\nopencode-ultrapress/\n├── src/\n│   ├── index.ts                    # Entry point, hook registration, plugin server\n│   ├── config/\n│   │   ├── schema.ts               # TypeScript type definitions (UltraPressConfig, etc.)\n│   │   └── defaults.ts             # Default config values + merge logic\n│   ├── layers/\n│   │   ├── layer1-output-filter.ts # RTK engine — routes tool output to domain filters\n│   │   ├── layer2-caveman.ts       # Semantic compression orchestrator\n│   │   ├── layer3-dcp.ts           # DCP orchestrator — nudge injection, pruning trigger\n│   │   └── layer4-cleanup.ts       # Auto-cleanup — dedup + error purging\n│   ├── filters/\n│   │   ├── git.ts                  # Git-specific output filter\n│   │   ├── test.ts                 # Test runner output filter\n│   │   ├── bash.ts                 # Shell output filter\n│   │   ├── fs.ts                   # Filesystem tool output filter\n│   │   └── generic.ts              # Fallback filter\n│   ├── dcp/\n│   │   ├── compress-state.ts       # In-memory state for pending compression blocks\n│   │   ├── compress-tool.ts        # ultrapress_compress tool definition \u0026 handler\n│   │   ├── context-monitor.ts      # Token usage monitoring + nudge logic\n│   │   ├── prune.ts                # Message removal + summary injection engine\n│   │   ├── protected-content.ts    # Defines which tool outputs are protected\n│   │   └── summary-store.ts        # Stores summaries for nesting support\n│   ├── caveman/\n│   │   ├── nlp.ts                  # Rule-based NLP compressor\n│   │   └── mlm.ts                  # MLM-based compressor (Transformers.js)\n│   ├── commands/\n│   │   └── slash.ts                # /up slash command handler\n│   └── utils/\n│       ├── token-count.ts          # Token estimation (char-based approximation)\n│       └── logger.ts               # Logging with configurable verbosity\n├── tests/\n│   ├── layer1.test.ts              # Output filter unit tests\n│   ├── layer2.test.ts              # Semantic compression unit tests\n│   └── layer3-dcp.test.ts          # DCP pruning + nudge unit tests\n├── benchmarks/\n│   ├── run.ts                      # Benchmark runner\n│   └── fixtures/                   # Benchmark test data\n├── docs/\n│   └── image/\n│       └── banner.svg              # README banner\n├── ultrapress.plugin.jsonc.example        # Configuration template (JSONC)\n├── tsconfig.json                   # TypeScript config\n├── tsup.config.ts                  # Build config (tsup)\n├── package.json\n├── CHANGELOG.md\n├── LICENSE\n└── README.md\n```\n\n### Hook Registration Map\n\n| OpenCode Hook | Trigger | UltraPress Handler | Layer |\n| :--- | :--- | :--- | :--- |\n| `tool.execute.after` | After CLI tool completes | Output filtering + token tracking + dedup | L1, L4 |\n| `chat.message` | Before user message is sent to LLM | Pruning pending blocks + semantic compression + nudge injection | L2, L3 |\n| `command.execute.before` | User types `/up` | Slash command handler | — |\n| `experimental.session.compacting` | OpenCode compacting session | Protected context injection | L4 |\n| `config` | Plugin initialization | Register `/up` command | — |\n| `tool` (definition) | Plugin init | Register `ultrapress_compress` tool | L3 |\n\n### Data Flow Detail\n\n```\n1. Plugin Init\n   config hook → register /up command\n   tool definition → register ultrapress_compress\n   load/migrate config from ~/.config/opencode/ultrapress.plugin.json\n\n2. Tool Execution (every tool call)\n   tool.execute.after → L1 processToolOutput()\n     → Domain routing (git→git.ts, test→test.ts, etc.)\n     → Middle-out truncation\n     → Deduplication\n   → L4 applyCleanup()\n     → Dedup check\n     → Error registration for purge\n\n3. Chat Message (every user message)\n   chat.message → L3 check pending compression blocks\n     → applyPruning() — remove old messages, inject summaries\n   → L2 processMessageContext() — semantic compression\n   → L3 context monitor — check token count\n     → if near limit → inject nudge prompt\n   → L3 turnTick() — update turn counter\n\n4. LLM calls ultrapress_compress\n   tool.execute.after → compress tool handler\n     → Create CompressionBlock in compress-state.ts\n     → Store summary for nesting\n   → Block will be executed on NEXT chat.message\n\n5. Session Compacting\n   session.compacting → L4 protected context injection\n```\n\n---\n\n## 🧪 Testing\n\nRun the entire test suite:\n\n```bash\nbun test\n```\n\n| Test File | Coverage | Layer |\n| :--- | :--- | :--- |\n| `tests/layer1.test.ts` | Output filtering: domain routing, truncation, tee save, dedup | L1 |\n| `tests/layer2.test.ts` | Semantic compression: NLP grammar stripping, code block protection, min length skip | L2 |\n| `tests/layer3-dcp.test.ts` | DCP: pruning with preserveLastN, nudge frequency, nesting summaries, protected content | L3 |\n\n```bash\n# Run specific layer\nbun test tests/layer1.test.ts\nbun test tests/layer2.test.ts\nbun test tests/layer3-dcp.test.ts\n\n# Run with TypeScript type checking\nbun run lint\n```\n\n---\n\n## 📊 Benchmark\n\nRun the full benchmark to measure the effectiveness of **all 4 layers**:\n\n```bash\nnpm run benchmark\n```\n\n### Latest Benchmark Results\n\n```\n┌────────────────────────────────┬──────────────────────────────────────────┬────────────┬──────────────┬──────────┐\n│Fixture                         │Layer                                     │Original    │Compressed    │Savings   │\n├────────────────────────────────┼──────────────────────────────────────────┼────────────┼──────────────┼──────────┤\n│git-diff-large.txt              │L1 — Git Filter                           │       1,657│           969│       42%│\n│npm-install-log.txt             │L1 — Generic Filter                       │         431│           430│        0%│\n│pytest-log.txt                  │L1 — Generic Filter                       │       1,200│         1,199│        0%│\n│chat-history.json               │L2 — NLP Semantic                         │         625│           490│       22%│\n│dcp-conversation.json           │L3 — DCP Pruning (14→summary)             │       2,347│           645│       73%│\n│                                │  ↳ 10 msg removed, 1 summary injected    │            │              │          │\n│3x identical npm test           │L4 — Tool Call Dedup                      │       2,244│           854│       62%│\n│                                │  ↳ 2 duplicates collapsed                │            │              │          │\n│5 errors × 6 turns              │L4 — Error Auto-Purge                     │         845│             0│      100%│\n│                                │  ↳ 5 errors purged after threshold       │            │              │          │\n└────────────────────────────────┴──────────────────────────────────────────┴────────────┴──────────────┴──────────┘\n\n✅ Total: 9,349 → 4,587 tokens (51% overall savings)\n```\n\n### Layer Summary\n\n| Layer | Fixture | Avg Savings | Characteristics |\n| :--- | :--- | :--- | :--- |\n| **L1** Output Filter | 3 fixtures | **21%** | Most effective for verbose CLI logs (`git diff`: 42%). Short output less affected. |\n| **L2** Semantic NLP | 1 fixture | **22%** | Consistently compresses natural language without destroying meaning. Code blocks fully protected. |\n| **L3** DCP Pruning | 1 fixture | **73%** | Biggest saver — removes 10 old messages \u0026 replaces with 1 summary. Compounding effect in long sessions. |\n| **L4** Auto Cleanup | 2 fixtures | **72%** | Dedup saves 62% from repeated tool calls. Error purge 100% after threshold. |\n\n\u003e 💡 **Insight**: L3 (DCP) is the layer with the highest savings because it removes old messages in bulk. In long sessions (100+ messages), the cumulative effect of L3 + L4 can reach **70-90% token savings**. Dataset and scripts are in [`benchmarks/`](./benchmarks/) — contribute fixtures from your stack for more representative results.\n\n---\n\n## 🚀 Local Development\n\n```bash\n# 1. Clone repository\ngit clone https://github.com/rahadiana/opencode-ultrapress.git\ncd opencode-ultrapress\n\n# 2. Install dependencies\nnpm install\n\n# 3. Build TypeScript\nnpm run build          # tsup — compile to dist/\n\n# 4. Development mode (watch)\nnpm run dev            # tsup --watch\n\n# 5. Run tests\nnpm test               # bun test\n\n# 6. Type checking\nnpm run lint           # tsc --noEmit\n\n# 7. Benchmark\nnpm run benchmark      # tsx benchmarks/run.ts\n```\n\n**Development Workflow:**\n\n1. Edit files in `src/`\n2. `npm run dev` for auto-rebuild\n3. `npm test` to verify\n4. Restart OpenCode to reload plugin\n5. Test via `/up stats` in chat\n\n---\n\n## ❓ FAQ \u0026 Troubleshooting\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003ePlugin doesn't appear after install\u003c/b\u003e\u003c/summary\u003e\n\n1. Ensure the plugin is registered in `~/.config/opencode/config.json`:\n   ```json\n   { \"plugins\": [\"@rahadiana/opencode-ultrapress\"] }\n   ```\n2. Restart OpenCode completely (not just reload window)\n3. Check if the package is installed: `npm list -g @rahadiana/opencode-ultrapress`\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eError \"Cannot find module @huggingface/transformers\"\u003c/b\u003e\u003c/summary\u003e\n\nMLM mode requires an additional dependency. Install manually:\n```bash\nnpm install -g @huggingface/transformers\n```\nOr switch to `\"nlp\"` mode which does not require external dependencies.\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eOpenCode feels slow after install\u003c/b\u003e\u003c/summary\u003e\n\n- Check semantic mode: `\"mode\": \"mlm\"` — MLM model loading at startup can be slow. Switch to `\"nlp\"` for zero latency.\n- Check `notification` level: `\"detailed\"` prints many logs. Set to `\"minimal\"`.\n- Ensure `minLengthChars` is not too low (default 250 is optimal).\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMy important messages were deleted by pruning\u003c/b\u003e\u003c/summary\u003e\n\n- Increase `preserveLastN` (default 4 → try 6 or 7)\n- Important tool output is automatically protected (`task`, `skill`, `todowrite`, `todoread`, `write`, `edit`)\n- Decision markers are also protected (`TODO`, `FIXME`, `ACTION ITEM`, `ROOT CAUSE`, `DECISION`, `BLOCKER`)\n- If still deleted, report as a bug with detailed logs\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eHow do I disable a specific layer?\u003c/b\u003e\u003c/summary\u003e\n\nSet `\"enabled\": false` on the layer you want to turn off:\n```json\n{\n  \"semantic\": { \"enabled\": false },\n  \"summarization\": { \"enabled\": false }\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eTypeScript error during development\u003c/b\u003e\u003c/summary\u003e\n\nEnsure dependencies are installed:\n```bash\nnpm install\nnpm run lint    # tsc --noEmit to check for type errors\n```\n\u003c/details\u003e\n\n---\n\n## 🗺 Roadmap\n\n| Feature | Status | Target |\n| :--- | :--- | :--- |\n| Layer 1: Domain-aware output filtering | ✅ Done | v0.1.0 |\n| Layer 2: NLP semantic compression | ✅ Done | v0.1.0 |\n| Layer 2: MLM mode | ⚠️ Experimental | v0.2.0 |\n| Layer 2: LLM mode (local summarization) | ✅ Done | v0.2.0 |\n| Layer 2: All-pairs MLM dedup | ✅ Done | v0.2.0 |\n| Layer 3: Block-based DCP pruning | ✅ Done | v0.1.0 |\n| Layer 3: `preserveLastN` protection | ✅ Done | v0.1.0 |\n| Layer 3: Multi-signal importance scoring | ✅ Done | v0.2.0 |\n| Layer 3: Reversible compression (`ultrapress_expand`) | ✅ Done | v0.2.0 |\n| Layer 3: Pre-emptive nudge @70% | ✅ Done | v0.2.0 |\n| Layer 3: Surgical message pruning | ✅ Done | v0.1.0 |\n| Layer 4: Error purging \u0026 dedup | ✅ Done | v0.1.0 |\n| `/up` slash commands | ✅ Done | v0.1.0 |\n| Real token tracking (OpenCode API) | ✅ Done | v0.2.0 |\n| Custom filter API | ✅ Done | v0.1.0 |\n| TF-IDF scoring (MLM improvement) | 🚧 Planned | v0.2.0 |\n| Sentence similarity (MLM improvement) | 🚧 Planned | v0.2.0 |\n| Sub-agent (`task`) token tracking \u0026 compression | 💡 Idea | TBD |\n| UI stats dashboard in OpenCode | 💡 Idea | TBD |\n| Support more languages (NLP) | 💡 Idea | TBD |\n\n---\n\n## 🤝 Contributing\n\nContributions are welcome! Areas most in need of help:\n\n1. **New Filters**: Add Layer 1 filters for new frameworks/stacks (Kubernetes, Docker, Terraform, Svelte, Flutter, etc.).\n2. **MLM Roadmap**: Help implement actual TF-IDF scoring or sentence similarity.\n3. **Benchmark Dataset**: Contribute fixture data from your tech stack.\n4. **Multi-language NLP**: Expand grammar stripping rules for more languages.\n5. **Bug Reports**: Report edge cases — poorly filtered tool output, important messages deleted, etc.\n\n### Development Setup\n\n```bash\ngit clone https://github.com/rahadiana/opencode-ultrapress.git\ncd opencode-ultrapress\nnpm install\nnpm run build\nnpm test\nnpm run benchmark\n```\n\n### Pull Request Process\n\n1. Fork repository\n2. Create feature branch (`git checkout -b feature/amazing-filter`)\n3. Commit changes (`git commit -m 'Add amazing filter'`)\n4. Push to branch (`git push origin feature/amazing-filter`)\n5. Open Pull Request — ensure `bun test` and `npm run lint` pass\n\n---\n\n## 📝 Changelog\n\nSee [CHANGELOG.md](./CHANGELOG.md) for the full version history.\n\n---\n\n## 📄 License\n\nMIT © [rahadiana](https://github.com/rahadiana)\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**UltraPress** — *Because tokens are expensive, but context is priceless.* ❤️\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frahadiana%2Fopencode-ultrapress","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frahadiana%2Fopencode-ultrapress","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frahadiana%2Fopencode-ultrapress/lists"}