{"_id":"@bulga138/kevin","name":"@bulga138/kevin","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@bulga138/kevin","version":"0.0.1","description":"KEVIN — context offloading for OpenCode.","type":"module","main":"dist/index.js","exports":{".":"./dist/index.js"},"bin":{"kevin":"dist/cli.js"},"engines":{"bun":">=1.3.13"},"scripts":{"build":"bun build src/index.ts --outfile dist/index.js --target bun --external @opencode-ai/plugin && bun build src/cli/main.ts --outfile dist/cli.js --target bun --banner '#!/usr/bin/env bun'","typecheck":"tsc --noEmit","test":"bun test"},"keywords":["opencode","opencode-plugin","context","tokens","kevin"],"author":{"name":"bulga138"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bulga138/kevin.git"},"bugs":{"url":"https://github.com/bulga138/kevin/issues"},"homepage":"https://github.com/bulga138/kevin#readme","peerDependencies":{"@opencode-ai/plugin":">=0.1.0"},"devDependencies":{"@types/node":"22.19.0","bun-types":"1.3.13","typescript":"5.9.3"},"gitHead":"dcb20bbcaf9c579259acecb9b8c7bfda42ffd917","_id":"@bulga138/kevin@0.0.1","_nodeVersion":"22.14.0","_npmVersion":"11.7.0","dist":{"integrity":"sha512-IWTILPexFZ2szF4MTSxAJwIcaLb21oeC5p+HziLOlup/4P6MBMciopPyK6/DkDtrQ9Ur+BXmVVNu4LdO+8heUQ==","shasum":"66cb36199ec958d20a52ce2952ce8b7096926a7c","tarball":"https://registry.npmjs.org/@bulga138/kevin/-/kevin-0.0.1.tgz","fileCount":5,"unpackedSize":94422,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCXXbU1vZI1/tbmItotPEbUvERhKvdD9lNFgQ9yuejUiAIgVValEHxoiuKYqT9gD8yt5eq06j5YP3LjTrLYPUyWru8="}]},"_npmUser":{"name":"bulga138","email":"bulga.sl@hotmail.com"},"directories":{},"maintainers":[{"name":"bulga138","email":"bulga.sl@hotmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/kevin_0.0.1_1777576259031_0.1547471626876733"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-30T19:10:58.922Z","0.0.1":"2026-04-30T19:10:59.180Z","modified":"2026-04-30T19:10:59.441Z"},"maintainers":[{"name":"bulga138","email":"bulga.sl@hotmail.com"}],"description":"KEVIN — context offloading for OpenCode.","homepage":"https://github.com/bulga138/kevin#readme","keywords":["opencode","opencode-plugin","context","tokens","kevin"],"repository":{"type":"git","url":"git+https://github.com/bulga138/kevin.git"},"author":{"name":"bulga138"},"bugs":{"url":"https://github.com/bulga138/kevin/issues"},"license":"MIT","readme":"# KEVIN\r\n\r\n**K**eeps **E**very **V**erbose **I**tem **N**arrow — context offloading plugin for [OpenCode](https://opencode.ai).\r\n\r\nIntercepts large tool outputs, writes them to disk, and replaces them with compact pointers like:\r\n\r\n```\r\n[fw A1B2C3D4] bun test e=1 45K 891L | 2 failed, 48 passed | retrieve with context_open tool\r\n```\r\n\r\nSaves ~80% of context tokens on typical coding sessions.\r\n\r\n---\r\n\r\n## Installation\r\n\r\n```jsonc\r\n// opencode.json\r\n{\r\n  \"plugin\": [\"@bulga138/kevin\"],\r\n}\r\n```\r\n\r\nOr one-liner:\r\n\r\n```sh\r\nbash <(curl -fsSL https://raw.githubusercontent.com/bulga138/kevin/main/install.sh)\r\n```\r\n\r\n---\r\n\r\n## How it works\r\n\r\n1. **`tool.execute.after`** — captures every tool output above the inline threshold (default 512 B) and writes it to `~/.cache/opencode/kevin/{session}/`\r\n2. **`chat.message`** — before the LLM sees each message, replaces stored outputs with compact `[fw XXXXXXXX]` pointers\r\n3. **`experimental.session.compacting`** — injects the offload index into context compaction summaries so the model survives context resets\r\n4. **`experimental.chat.system.transform`** — keeps the model passively aware of how many outputs are offloaded\r\n\r\n---\r\n\r\n## Output tiers\r\n\r\n| Size         | Exit code     | What the model sees     |\r\n| ------------ | ------------- | ----------------------- |\r\n| < 512 B      | any           | Full output (inline)    |\r\n| 512 B – 4 KB | any           | Compact pointer only    |\r\n| > 4 KB       | 0 (success)   | Compact pointer only    |\r\n| > 4 KB       | ≠ 0 (failure) | Pointer + last 10 lines |\r\n\r\n---\r\n\r\n## Tools available to the model\r\n\r\n| Tool             | Description                                                                             |\r\n| ---------------- | --------------------------------------------------------------------------------------- |\r\n| `context_open`   | Retrieve offloaded content by ID. Supports `start_line` / `end_line` for slicing.       |\r\n| `context_recent` | List recent offloaded outputs. Supports `tags` filter.                                  |\r\n| `context_search` | Full-text search — plain substring or `/regex/flags`. Returns ±context lines per match. |\r\n| `context_save`   | Manually offload arbitrary content with a label.                                        |\r\n| `context_export` | Export current session as markdown.                                                     |\r\n| `context_pin`    | Pin an entry to prevent LRU/TTL eviction.                                               |\r\n| `kevin_unpin`    | Remove a pin.                                                                           |\r\n| `kevin_note`     | Attach a free-text note to an entry.                                                    |\r\n| `kevin_tag`      | Add or remove tags on an entry.                                                         |\r\n| `kevin_diff`     | Unified diff between two offloaded outputs.                                             |\r\n| `kevin_timeline` | Chronological view of all offloaded outputs for the session.                            |\r\n| `kevin_doctor`   | Diagnostics: storage health, config, orphaned files.                                    |\r\n| `kevin_stats`    | Token-saving statistics for the current session.                                        |\r\n\r\n---\r\n\r\n## Configuration\r\n\r\nAll options can be set via environment variables (`KEVIN_*`) or a config file at `~/.config/kevin/config.json` (or `.kevin/config.json` in the project directory). Environment variables take precedence.\r\n\r\n| Env var                      | Config key           | Default | Description                                                                          |\r\n| ---------------------------- | -------------------- | ------- | ------------------------------------------------------------------------------------ |\r\n| `KEVIN_INLINE_MAX`           | `inline_max`         | `512`   | Bytes below which output is kept inline                                              |\r\n| `KEVIN_COMPACT_MAX`          | `compact_max`        | `4096`  | Upper boundary of compact-pointer tier                                               |\r\n| `KEVIN_MAX_CACHE_MB`         | `max_cache_mb`       | `250`   | Cache ceiling before LRU eviction                                                    |\r\n| `KEVIN_TTL_SUCCESS_H`        | `ttl_success_h`      | `24`    | Hours before successful outputs expire (0 = never)                                   |\r\n| `KEVIN_TTL_FAILURE_H`        | `ttl_failure_h`      | `48`    | Hours before failed outputs expire (0 = never)                                       |\r\n| `KEVIN_DISABLE`              | `disabled`           | `false` | Completely disable offloading                                                        |\r\n| `KEVIN_DISABLE_REDACTION`    | `disable_redaction`  | `false` | Skip secret redaction                                                                |\r\n| `KEVIN_AUTO_PIN_FAILURES`    | `auto_pin_failures`  | `true`  | Auto-pin failed outputs                                                              |\r\n| `KEVIN_AUTO_PIN_MIN_BYTES`   | `auto_pin_min_bytes` | `0`     | Auto-pin outputs above this size (0 = off)                                           |\r\n| `KEVIN_AUTO_PIN_TOOLS`       | `auto_pin_tools`     | `\"\"`    | Comma-separated tool names to always pin                                             |\r\n| `KEVIN_POINTER_FORMAT`       | `pointer_format`     | `\"\"`    | Custom pointer template (tokens: `{id}` `{tool}` `{size}` `{lines}` `{exit}` `{ts}`) |\r\n| `KEVIN_INTERCEPT_MCP`        | `intercept_mcp`      | `false` | Intercept MCP tool outputs                                                           |\r\n| `KEVIN_DISABLE_UPDATE_CHECK` | `disable_check`      | `false` | Disable automatic update check                                                       |\r\n\r\nLegacy `FEWWORD_*` prefixes are accepted as fallbacks for all options.\r\n\r\n### Config file example\r\n\r\n```json\r\n{\r\n  \"inline_max\": 1024,\r\n  \"compact_max\": 8192,\r\n  \"ttl_success_h\": 12,\r\n  \"ttl_failure_h\": 72,\r\n  \"auto_pin_failures\": true,\r\n  \"auto_pin_tools\": [\"test\", \"build\"]\r\n}\r\n```\r\n\r\n### Per-project disable\r\n\r\n```sh\r\nmkdir -p .kevin && touch .kevin/DISABLE_OFFLOAD\r\n```\r\n\r\n---\r\n\r\n## Secret redaction\r\n\r\nBefore writing any output to disk, KEVIN automatically redacts:\r\n\r\n- AWS access keys (`AKIA…`)\r\n- AWS secret keys\r\n- GitHub tokens (`ghp_`, `gho_`, `ghs_`, `ghr_`, `github_pat_`)\r\n- OpenAI / Anthropic keys (`sk-…`)\r\n- Generic key/token/secret/password assignments\r\n- Bearer tokens in HTTP headers\r\n- JWTs\r\n- PEM private keys\r\n\r\nMatched text is replaced with `[REDACTED:{type}]` so surrounding context is preserved.\r\n\r\nDisable with `KEVIN_DISABLE_REDACTION=1` (not recommended).\r\n\r\n---\r\n\r\n## Storage layout\r\n\r\n```\r\n~/.cache/opencode/kevin/\r\n├── {SESSION_ID}/\r\n│   ├── {ID}.txt           # full (redacted) output\r\n│   ├── {ID}.meta.json     # tool, exitCode, bytes, lines, hash, signature, tags, note…\r\n│   ├── LATEST.txt         # symlink → most recent output\r\n│   └── LATEST_{tool}.txt  # symlink → most recent output per tool\r\n└── stats.json\r\n```\r\n\r\n---\r\n\r\n## CLI\r\n\r\n```sh\r\nkevin stats    # token savings for the current session\r\nkevin list     # list all offloaded outputs\r\nkevin clear    # clear the cache\r\nkevin migrate  # backfill metadata fields from old versions\r\n```\r\n\r\n---\r\n\r\n## Architecture\r\n\r\n```\r\nsrc/\r\n├── index.ts                  # Integration layer — hook/tool wiring only\r\n├── config.ts                 # Env var + config file resolution\r\n├── redaction.ts              # Secret pattern matching and redaction\r\n├── pointer.ts                # Re-exports from offloadService (backward compat)\r\n├── stats.ts                  # Session statistics sidecar\r\n├── update-check.ts           # GitHub API update check\r\n├── transport/\r\n│   ├── fileTransport.ts      # FileTransport interface\r\n│   │                           NodeFileTransport (production)\r\n│   │                           MemoryTransport (tests)\r\n│   └── index.ts\r\n├── domain/\r\n│   └── offloadService.ts     # ALL business logic (offload, search, diff, TTL…)\r\n└── cli/\r\n    ├── main.ts               # CLI dispatcher\r\n    └── commands/\r\n        ├── stats.ts\r\n        ├── list.ts\r\n        ├── clear.ts\r\n        └── migrate.ts\r\n```\r\n\r\n**Dependency flow:** `index.ts` → `offloadService` → `transport`. No cycles.\r\n\r\n---\r\n\r\nInspired by [FewWord](https://github.com/sheeki03/Few-Word) (Claude Code plugin),\r\n\r\n## License\r\n\r\nMIT\r\n","readmeFilename":"README.md","_rev":"1-7f2a352d54fb27a32122c48a03f1ca89"}