npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opencode-adaptive-snip

v0.7.7

Published

OpenCode plugin that learns verbose shell commands and automatically prefixes them with snip.

Readme

opencode-adaptive-snip

AI 驱动的 Shell 命令预处理插件 — 自动学习哪些命令输出过多,自动注入 snip 前缀来压缩命令输出。

English | 中文


中文

做什么

opencode-adaptive-snip 是 OpenCode 插件,两个核心功能:

  1. Adaptive Snip 模式 — 拦截所有 bash 工具调用,匹配规则表,自动注入 snip 前缀
  2. Analyze 模式 — 通过 tool.execute.after 收集 bash 执行结果,批量发给 LLM 分析,自动生成 snip 规则

架构

┌─────────────────────────────────────────────────────┐
│                    OpenCode Runtime                 │
├─────────────────────────────────────────────────────┤
│  tool.execute.before (snip.ts)                      │
│  ├─ 拦截 bash 命令                                  │
│  ├─ 规则匹配 (config → learned → fallback)           │
│  ├─ 注入 snip 前缀                                  │
│  └─ 处理 pipe / operator / env var                  │
├─────────────────────────────────────────────────────┤
│  tool.execute.after (command-tracker.ts)             │
│  ├─ tool.execute.before 记录 command + timestamp     │
│  ├─ tool.execute.after 配对 output                   │
│  └─ 触发 analyze()                                  │
├─────────────────────────────────────────────────────┤
│  LearnAnalyzer (analyze.ts)                         │
│  ├─ 缓冲 command/output 对                          │
│  ├─ 构建 prompt → LLM 分析                          │
│  ├─ 解析 JSON → mergeLearned()                      │
│  └─ 存储到 .opencode/snip-rules.json                │
└─────────────────────────────────────────────────────┘

Agent 兼容性

插件完全兼容 OpenCode 所有 agent 模式,包括 Sisyphus、Ultraworker 等。

  • tool.execute.before / tool.execute.after hooks 运行在 OpenCode 运行时层,不依赖特定 agent
  • 分析 session 通过独立的 client.session.create() 创建,不使用 agent context 的模型
  • 配置 analyze.llmModel 时,该模型会同时传给 session.create()session.prompt(),确保一致性
  • 如果分析 LLM 需要指定模型,通过 analyze.llmModel 配置覆盖
  • 仅有的限制:如果同时使用了多个插件链,adaptive-snip 的 output truncation (5000 chars) 可能被后续插件覆盖。推荐将 adaptive-snip 放在 opencode.json plugins 数组前列

安装

# 在 OpenCode 项目目录
opencode plugin opencode-adaptive-snip

该命令会安装 npm 包并自动更新 OpenCode 配置。需要安装到全局配置时使用:

opencode plugin --global opencode-adaptive-snip

配置

推荐方式:独立配置文件

插件自动发现项目根目录下的 adaptive-snip.json。使用 opencode plugin opencode-adaptive-snip 安装后,规则配置无需写入 opencode.json

adaptive-snip.json(放在项目根目录):

{
  "analyze": {
    "enabled": true,
    "autoLearn": true,
    "batchSize": 20,
    "minConfidence": 0.7,
    "maxRules": 50,
    "cooldownMinutes": 60
  },
  "rules": [
    { "pattern": "^go test", "snip": "snip --timeout 120", "source": "config" },
    { "pattern": "^npm (install|test|run build)", "snip": "snip", "source": "config" }
  ],
  "fallback": { "prefix": "snip" },
  "ruleFile": ".opencode/snip-rules.json"
}

opencode.json

{
  "plugin": [
    "opencode-adaptive-snip"
  ]
}

配置优先级:opencode.json 内联 options > adaptive-snip.json > 默认值

备选方式:内联配置

opencode.json 中直接写配置(仍支持):

{
  "plugin": [
    [
      "opencode-adaptive-snip",
      {
        // 分析模式配置
        "analyze": {
          "enabled": true,         // 启用分析模式
          "autoLearn": true,       // 自动学习规则
          "batchSize": 20,         // 每批分析的命令数量
          "minConfidence": 0.7,    // 最低置信度阈值
          "maxRules": 50,          // 最多保留的学习规则数
          "cooldownMinutes": 60,   // 两次分析的最小间隔(分钟)
          "llmModel": {            // 可选:指定分析用的模型
            "providerID": "openai",
            "modelID": "gpt-4o"
          }
        },

        // 手动规则(最高优先级)
        "rules": [
          {
            "pattern": "^go test",
            "snip": "snip --timeout 120",
            "flags": "i",
            "description": "Go 测试输出通常很长",
            "source": "config"
          },
          {
            "pattern": "^npm (install|test|run build)",
            "snip": "snip",
            "source": "config"
          }
        ],

        // 回退 snip 配置(当无规则匹配时使用)
        "fallback": {
          "prefix": "snip"
        },

        // 学习规则存储路径(相对于项目根目录)
        "ruleFile": ".opencode/snip-rules.json"
      }
    ]
  ]
}

完整配置示例

以下是一个生产环境级别的完整配置,包含所有选项和针对常见工具链的手动规则。保存为项目根目录的 adaptive-snip.json 即可自动生效。

{
  // ═══════════════════════════════════════════
  // 分析模式 — LLM 自动学习新规则
  // ═══════════════════════════════════════════
  "analyze": {
    "enabled": true,
    "autoLearn": true,
    "batchSize": 20,
    "minConfidence": 0.7,
    "maxRules": 50,
    "cooldownMinutes": 60,
    // 不指定 llmModel → 使用 OpenCode 当前会话模型
  },

  // ═══════════════════════════════════════════
  // 手动规则(最高优先级,不会被学习覆盖)
  // ═══════════════════════════════════════════
  "rules": [
    // ── Go ──────────────────────────────
    {
      "pattern": "^go (test|bench|build|vet|lint|mod tidy)",
      "snip": "snip --timeout 120",
      "description": "Go 工具链 — 测试/构建输出冗长",
      "source": "config"
    },
    {
      "pattern": "^go run",
      "snip": "snip --timeout 60",
      "description": "go run 可能输出运行时日志",
      "source": "config"
    },

    // ── Node / npm ──────────────────────
    {
      "pattern": "^npm (install|ci|test|run build|run lint|run dev)",
      "snip": "snip --timeout 120",
      "description": "npm 安装/测试/构建输出",
      "source": "config"
    },
    {
      "pattern": "^npx (vitest|jest|eslint|prettier|tsc|playwright)",
      "snip": "snip --timeout 120",
      "description": "npx 运行测试/lint/构建工具",
      "source": "config"
    },
    {
      "pattern": "^pnpm (install|test|build|lint)",
      "snip": "snip --timeout 120",
      "source": "config"
    },
    {
      "pattern": "^yarn (install|test|build|lint)",
      "snip": "snip --timeout 120",
      "source": "config"
    },
    {
      "pattern": "^bun (install|test|run build|run lint)",
      "snip": "snip --timeout 120",
      "description": "Bun 包管理/测试",
      "source": "config"
    },

    // ── TypeScript ──────────────────────
    {
      "pattern": "^tsc",
      "snip": "snip --timeout 60",
      "description": "TypeScript 编译错误列表可能很长",
      "source": "config"
    },

    // ── Python ──────────────────────────
    {
      "pattern": "^(python3?|python) (-m )?(pytest|unittest|mypy|ruff|black|isort)",
      "snip": "snip --timeout 120",
      "description": "Python 测试/lint/格式化",
      "source": "config"
    },
    {
      "pattern": "^pip (install|freeze|list)",
      "snip": "snip --timeout 60",
      "description": "pip 安装/列表输出",
      "source": "config"
    },
    {
      "pattern": "^(uv|poetry) (run|add|install|lock|build)",
      "snip": "snip --timeout 120",
      "description": "Python 现代包管理工具",
      "source": "config"
    },

    // ── Rust ────────────────────────────
    {
      "pattern": "^cargo (test|build|check|clippy|bench|doc)",
      "snip": "snip --timeout 180",
      "description": "Rust 编译/测试输出极长",
      "source": "config"
    },
    {
      "pattern": "^rustc",
      "snip": "snip --timeout 120",
      "source": "config"
    },

    // ── C/C++ ──────────────────────────
    {
      "pattern": "^(make|cmake|ninja|gcc|g\\+\\+|clang\\+\\+)",
      "snip": "snip --timeout 180",
      "description": "C/C++ 编译输出可能极长",
      "source": "config"
    },

    // ── Java / JVM ──────────────────────
    {
      "pattern": "^(mvn|gradle|./gradlew|./mvnw)",
      "snip": "snip --timeout 180",
      "description": "Maven/Gradle 构建输出",
      "source": "config"
    },

    // ── Docker ──────────────────────────
    {
      "pattern": "^docker (build|compose|logs|ps|images|system)",
      "snip": "snip --timeout 120",
      "description": "Docker 构建/日志",
      "source": "config"
    },

    // ── Kubernetes ──────────────────────
    {
      "pattern": "^kubectl (get|describe|logs|apply|delete)",
      "snip": "snip --timeout 60",
      "description": "K8s 资源查询/操作",
      "source": "config"
    },
    {
      "pattern": "^(helm|k9s|kustomize|argocd)",
      "snip": "snip --timeout 60",
      "source": "config"
    },

    // ── Git ─────────────────────────────
    {
      "pattern": "^git (log|diff|show|blame|status|branch)",
      "snip": "snip --timeout 30",
      "description": "Git 查询命令 — 日志/diff 可能很长",
      "source": "config"
    },

    // ── Shell 批处理 ────────────────────
    {
      "pattern": "^(find|locate|tree|du|df|ls -l)",
      "snip": "snip --timeout 30",
      "description": "文件系统查询 — 大目录输出极多",
      "source": "config"
    },
    {
      "pattern": "^(ps|top|htop|netstat|ss|lsof)",
      "snip": "snip --timeout 30",
      "description": "系统进程/网络查询",
      "source": "config"
    },
    {
      "pattern": "^curl.*(-v|--verbose|--trace)",
      "snip": "snip --timeout 30",
      "description": "curl verbose 输出",
      "source": "config"
    },

    // ── 包管理器通用 ────────────────────
    {
      "pattern": "^(apt|apt-get|brew|dnf|yum|pacman|zypper) (install|update|upgrade|search|list)",
      "snip": "snip --timeout 120",
      "description": "系统包管理安装/搜索",
      "source": "config"
    },

    // ── Linter / Formatter 类 ───────────
    {
      "pattern": "^(eslint|prettier|biome|oxlint|stylelint|clang-format)",
      "snip": "snip --timeout 60",
      "description": "Linter/Formatter 输出",
      "source": "config"
    },

    // ── 测试框架 ────────────────────────
    {
      "pattern": "^(vitest|jest|mocha|ava|tap|cypress|playwright test)",
      "snip": "snip --timeout 120",
      "description": "JS/TS 测试框架",
      "source": "config"
    },

    // ── 构建工具 ────────────────────────
    {
      "pattern": "^(webpack|vite|rollup|esbuild|turbo|nx|lage) (build|serve|dev)",
      "snip": "snip --timeout 120",
      "description": "前端构建工具",
      "source": "config"
    }
  ],

  // ═══════════════════════════════════════════
  // 无规则匹配时:给所有命令自动加 "snip"
  // 设为 null 则不加
  // ═══════════════════════════════════════════
  "fallback": {
    "prefix": "snip"
  },

  // ═══════════════════════════════════════════
  // 学习规则存储路径
  // ═══════════════════════════════════════════
  "ruleFile": ".opencode/snip-rules.json"
}

💡 使用建议:将此配置放入项目根目录的 adaptive-snip.json,然后根据你的项目技术栈增删 rules 中的条目。LLM 分析会自动补全你遗漏的规则。你手动配置的规则永远不会被 LLM 覆写。

配置项详解

analyze — 分析模式

| 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | enabled | boolean | false | 是否启用 LLM 分析 | | autoLearn | boolean | false | 自动保存学习到的规则(依赖 enabled) | | batchSize | number | 20 | 积累多少个命令/输出对后触发分析 | | minConfidence | number | 0.7 | 置信度阈值,低于此值的建议不保存 | | maxRules | number | 50 | 学习规则数量上限 | | cooldownMinutes | number | 60 | 两次分析之间的冷却时间 | | llmModel | object | — | 指定 LLM 模型,不指定则使用 OpenCode 默认模型 | | notification | "off" \| "minimal" \| "detailed" | "minimal" | 通知级别:off=不通知, minimal=仅分析摘要, detailed=完整分析报告 |

控制分析模式有三种方式:

// 1. 禁用(默认)
{ "analyze": false }

// 2. 启用全默认
{ "analyze": true }

// 3. 自定义
{
  "analyze": {
    "enabled": true,
    "autoLearn": true,
    "batchSize": 30,
    "llmModel": { "providerID": "anthropic", "modelID": "claude-sonnet-4-20250514" }
  }
}

rules — 手动规则

用户手动配置的规则,优先级最高,不会被 LLM 学习覆盖。

{
  "rules": [
    {
      "pattern": "^go (test|build|vet)",   // 正则表达式
      "snip": "snip --timeout 120",        // snip 命令及参数
      "flags": "i",                        // 可选:正则标志
      "description": "Go 工具链输出",       // 可选:说明
      "source": "config"                   // 必须为 "config"
    }
  ]
}

字段说明:

| 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | pattern | string | ✅ | 正则表达式,匹配命令文本 | | snip | string | ✅ | snip 命令前缀,如 "snip""snip --timeout 120" | | flags | string | ❌ | 正则标志,如 "i"(不区分大小写) | | description | string | ❌ | 规则说明 | | source | "config" | ✅ | 必须为 "config" |

pattern — 正则安全建议

Rule 的 pattern 字段使用 JavaScript 正则表达式。避免复杂组合导致性能问题:

  • 避免复杂 lookahead/backreference — 可能导致灾难性回溯
  • 优先用简单锚定模式^python(3)? (-m )?(pytest|...) 优于 (?=[ ;&\|])python
  • 实际测试你的 pattern 是否能匹配预期命令

插件会做基础 ReDoS 校验(嵌套量词如 (a+)+ 会被拒绝),但保持 pattern 简单始终更安全。

fallback — 回退前缀

当无规则匹配时,可以选择性使用回退 snip:

// 无匹配 → 不加 snip(默认)
{ "fallback": null }

// 无匹配 → 加 "snip" 前缀
{ "fallback": { "prefix": "snip" } }

// 无匹配 → 加 "snip --timeout 60" 前缀
{ "fallback": { "prefix": "snip --timeout 60" } }

llmModel — 指定分析模型

llmModelAnalyzeConfig 下的一个字段,它让你可以指定用什么模型来进行 LLM 分析,而不依赖 OpenCode 的默认模型。当不指定时,插件使用 OpenCode 的默认模型(当前会话模型)。

{
  "analyze": {
    "enabled": true,
    "autoLearn": true,
    "llmModel": {
      "providerID": "openai",    // 提供商标识
      "modelID": "gpt-4o"        // 模型标识
    }
  }
}

providerIDmodelID 的具体值取决于你的 OpenCode 配置了哪些 provider。例如:

  • { "providerID": "anthropic", "modelID": "claude-sonnet-4-20250514" }
  • { "providerID": "openai", "modelID": "gpt-4o" }

规则优先级

手动配置规则 (source: "config")
  ↓ 无匹配
学习规则 (source: "learned")
  ↓ 无匹配
回退前缀 (fallback.prefix)
  ↓ 无匹配
不加前缀

规则文件格式

学习规则自动保存到 .opencode/snip-rules.json

{
  "version": 1,
  "rules": [
    {
      "pattern": "^npm test",
      "snip": "snip",
      "confidence": 0.92,
      "description": "npm test 输出包含大量测试日志",
      "source": "learned",
      "createdAt": "2026-05-09T10:30:00.000Z",
      "updatedAt": "2026-05-09T10:30:00.000Z"
    }
  ],
  "analytics": {
    "lastAnalysisAt": "2026-05-09T10:30:00.000Z",
    "totalSuggestionsLearned": 25
  }
}

你可以手动编辑此文件来调整学习规则。

行为细节

不会加 snip 前缀的命令

  • Shell 内置命令/敏感 shell 命令cd, export, alias, source, echo, pwd
  • 已经包含 snip 前缀 的命令(防重复注入)
  • 环境变量前缀 被保留:FOO=bar cmdFOO=bar snip cmd

Pipe 和 Operator 处理

# 输入
npm test | grep FAIL

# 输出(仅第一个命令前加 snip)
snip npm test | grep FAIL
# 输入
go build && go test ./...

# 输出(匹配规则的非 operator 段会加 snip)
snip --timeout 120 go build && snip --timeout 120 go test ./...

输出截断

分析模式下,命令输出超过 5000 字符会被截断后再发给 LLM。

调试

插件在控制台输出 [adaptive-snip] 前缀的日志:

  • [adaptive-snip] LLM analysis failed: — 分析失败(非致命)
  • [adaptive-snip] Unpaired shell.ended event — 事件配对异常
  • [adaptive-snip] analyze.autoLearn=true but analyze.enabled=false — 配置冲突

开发

bun install
bun test
npm run typecheck

English

What It Does

opencode-adaptive-snip is an OpenCode plugin with two modes:

  1. Adaptive Snip — intercepts all bash tool calls, matches rules, auto-prepends snip prefix
  2. Analyze — collects bash execution results via tool.execute.after, buffers command/output pairs, sends batches to LLM for analysis, auto-generates snip rules

Architecture

┌─────────────────────────────────────────────────────┐
│                    OpenCode Runtime                 │
├─────────────────────────────────────────────────────┤
│  tool.execute.before (snip.ts)                      │
│  ├─ Intercept bash commands                         │
│  ├─ Rule matching (config → learned → fallback)     │
│  ├─ Inject snip prefix                              │
│  └─ Handle pipes / operators / env vars             │
├─────────────────────────────────────────────────────┤
│  tool.execute.after (command-tracker.ts)             │
│  ├─ tool.execute.before records command + timestamp  │
│  ├─ tool.execute.after pairs with output             │
│  └─ Trigger analyze()                               │
├─────────────────────────────────────────────────────┤
│  LearnAnalyzer (analyze.ts)                         │
│  ├─ Buffer command/output pairs                     │
│  ├─ Build prompt → LLM analysis                     │
│  ├─ Parse JSON → mergeLearned()                     │
│  └─ Save to .opencode/snip-rules.json               │
└─────────────────────────────────────────────────────┘

Agent Compatibility

The plugin is fully compatible with all OpenCode agent modes, including Sisyphus, Ultraworker, and others.

  • tool.execute.before / tool.execute.after hooks run at the OpenCode runtime layer, independent of any specific agent
  • Analysis sessions use independent client.session.create() calls, not the agent context's model
  • When analyze.llmModel is configured, it is passed to both session.create() and session.prompt() for consistency
  • To specify a model for analysis LLM, use the analyze.llmModel config override
  • The only limitation: if multiple plugins are chained, adaptive-snip's output truncation (5000 chars) may be overwritten by subsequent plugins. It is recommended to place adaptive-snip early in the opencode.json plugins array

Installation

# In your OpenCode project
opencode plugin opencode-adaptive-snip

This installs the npm package and updates your OpenCode config. To install it in global config:

opencode plugin --global opencode-adaptive-snip

Configuration

Recommended: Separate config file

The plugin auto-discovers adaptive-snip.json in the project root. After installing with opencode plugin opencode-adaptive-snip, rule configuration does not need to be placed in opencode.json.

adaptive-snip.json (place in project root):

{
  "analyze": {
    "enabled": true,
    "autoLearn": true,
    "batchSize": 20,
    "minConfidence": 0.7,
    "maxRules": 50,
    "cooldownMinutes": 60
  },
  "rules": [
    { "pattern": "^go test", "snip": "snip --timeout 120", "source": "config" },
    { "pattern": "^npm (install|test|run build)", "snip": "snip", "source": "config" }
  ],
  "fallback": { "prefix": "snip" },
  "ruleFile": ".opencode/snip-rules.json"
}

opencode.json:

{
  "plugin": [
    "opencode-adaptive-snip"
  ]
}

Merge priority: opencode.json inline options > adaptive-snip.json > defaults.

Alternative: Inline config

Add config directly in opencode.json (still supported):

{
  "plugin": [
    [
      "opencode-adaptive-snip",
      {
        "analyze": {
          "enabled": true,
          "autoLearn": true,
          "batchSize": 20,
          "minConfidence": 0.7,
          "maxRules": 50,
          "cooldownMinutes": 60,
          "llmModel": {
            "providerID": "openai",
            "modelID": "gpt-4o"
          }
        },
        "rules": [
          {
            "pattern": "^go test",
            "snip": "snip --timeout 120",
            "source": "config"
          }
        ],
        "fallback": { "prefix": "snip" },
        "ruleFile": ".opencode/snip-rules.json"
      }
    ]
  ]
}

Complete Configuration Example

Below is a production-grade configuration with all options and manually-crafted rules for common toolchains. Save as adaptive-snip.json in your project root — it's auto-discovered.

{
  // ═══════════════════════════════════════════
  // Analyze mode — LLM learns new rules automatically
  // ═══════════════════════════════════════════
  "analyze": {
    "enabled": true,
    "autoLearn": true,
    "batchSize": 20,
    "minConfidence": 0.7,
    "maxRules": 50,
    "cooldownMinutes": 60
    // Omit llmModel → uses OpenCode current session model
  },

  // ═══════════════════════════════════════════
  // Manual rules (highest priority, never overwritten by learning)
  // ═══════════════════════════════════════════
  "rules": [
    // ── Go ──────────────────────────────
    { "pattern": "^go (test|bench|build|vet|lint|mod tidy)", "snip": "snip --timeout 120", "description": "Go toolchain", "source": "config" },
    { "pattern": "^go run", "snip": "snip --timeout 60", "source": "config" },

    // ── Node / npm ──────────────────────
    { "pattern": "^npm (install|ci|test|run build|run lint|run dev)", "snip": "snip --timeout 120", "source": "config" },
    { "pattern": "^npx (vitest|jest|eslint|prettier|tsc|playwright)", "snip": "snip --timeout 120", "source": "config" },
    { "pattern": "^pnpm (install|test|build|lint)", "snip": "snip --timeout 120", "source": "config" },
    { "pattern": "^yarn (install|test|build|lint)", "snip": "snip --timeout 120", "source": "config" },
    { "pattern": "^bun (install|test|run build|run lint)", "snip": "snip --timeout 120", "source": "config" },

    // ── TypeScript ──────────────────────
    { "pattern": "^tsc", "snip": "snip --timeout 60", "description": "TypeScript compiler", "source": "config" },

    // ── Python ──────────────────────────
    { "pattern": "^(python3?|python) (-m )?(pytest|unittest|mypy|ruff|black|isort)", "snip": "snip --timeout 120", "source": "config" },
    { "pattern": "^pip (install|freeze|list)", "snip": "snip --timeout 60", "source": "config" },
    { "pattern": "^(uv|poetry) (run|add|install|lock|build)", "snip": "snip --timeout 120", "source": "config" },

    // ── Rust ────────────────────────────
    { "pattern": "^cargo (test|build|check|clippy|bench|doc)", "snip": "snip --timeout 180", "source": "config" },
    { "pattern": "^rustc", "snip": "snip --timeout 120", "source": "config" },

    // ── C/C++ ──────────────────────────
    { "pattern": "^(make|cmake|ninja|gcc|g\\+\\+|clang\\+\\+)", "snip": "snip --timeout 180", "source": "config" },

    // ── Java / JVM ──────────────────────
    { "pattern": "^(mvn|gradle|./gradlew|./mvnw)", "snip": "snip --timeout 180", "source": "config" },

    // ── Docker ──────────────────────────
    { "pattern": "^docker (build|compose|logs|ps|images|system)", "snip": "snip --timeout 120", "source": "config" },

    // ── Kubernetes ──────────────────────
    { "pattern": "^kubectl (get|describe|logs|apply|delete)", "snip": "snip --timeout 60", "source": "config" },
    { "pattern": "^(helm|k9s|kustomize|argocd)", "snip": "snip --timeout 60", "source": "config" },

    // ── Git ─────────────────────────────
    { "pattern": "^git (log|diff|show|blame|status|branch)", "snip": "snip --timeout 30", "source": "config" },

    // ── Shell batch queries ─────────────
    { "pattern": "^(find|locate|tree|du|df|ls -l)", "snip": "snip --timeout 30", "source": "config" },
    { "pattern": "^(ps|top|htop|netstat|ss|lsof)", "snip": "snip --timeout 30", "source": "config" },
    { "pattern": "^curl.*(-v|--verbose|--trace)", "snip": "snip --timeout 30", "source": "config" },

    // ── System package managers ─────────
    { "pattern": "^(apt|apt-get|brew|dnf|yum|pacman|zypper) (install|update|upgrade|search|list)", "snip": "snip --timeout 120", "source": "config" },

    // ── Linter / Formatter ──────────────
    { "pattern": "^(eslint|prettier|biome|oxlint|stylelint|clang-format)", "snip": "snip --timeout 60", "source": "config" },

    // ── Test frameworks ─────────────────
    { "pattern": "^(vitest|jest|mocha|ava|tap|cypress|playwright test)", "snip": "snip --timeout 120", "source": "config" },

    // ── Build tools ─────────────────────
    { "pattern": "^(webpack|vite|rollup|esbuild|turbo|nx|lage) (build|serve|dev)", "snip": "snip --timeout 120", "source": "config" }
  ],

  // ═══════════════════════════════════════════
  // Fallback: auto-prefix "snip" for unmatched commands
  // Set null to disable
  // ═══════════════════════════════════════════
  "fallback": { "prefix": "snip" },

  // ═══════════════════════════════════════════
  // Learned rules storage path
  // ═══════════════════════════════════════════
  "ruleFile": ".opencode/snip-rules.json"
}

💡 Tip: Drop this into adaptive-snip.json in your project root, then add/remove rules entries to match your tech stack. The LLM analyzer will fill in gaps automatically. Your manual config rules are never overwritten.

Options Reference

analyze

| Field | Type | Default | Description | |-------|------|---------|-------------| | enabled | boolean | false | Enable LLM analysis | | autoLearn | boolean | false | Auto-save learned rules | | batchSize | number | 20 | Pairs to buffer before analysis | | minConfidence | number | 0.7 | Minimum confidence threshold | | maxRules | number | 50 | Maximum learned rules | | cooldownMinutes | number | 60 | Minimum interval between analyses | | llmModel | object | — | Override LLM model for analysis | | notification | "off" \| "minimal" \| "detailed" | "minimal" | Notification level: off=no output, minimal=analysis summary, detailed=full report |

rules

User-configured rules. Highest priority, never overwritten by learning.

| Field | Type | Required | Description | |-------|------|----------|-------------| | pattern | string | ✅ | Regex to match command text | | snip | string | ✅ | Snip prefix | | flags | string | ❌ | Regex flags (e.g. "i") | | description | string | ❌ | Human-readable description | | source | "config" | ✅ | Must be "config" |

pattern -- Regex Safety

Rule patterns use JavaScript regular expressions. Avoid complexity that could cause performance issues:

  • Avoid lookaheads/backreferences in complex combinations -- they can cause catastrophic backtracking
  • Prefer simple anchored patterns: ^python(3)? (-m )?(pytest|...) over (?=[ ;&\|])python
  • Test patterns with the actual commands you expect to match

The plugin validates patterns for basic ReDoS safety (nested quantifiers like (a+)+ are rejected), but simpler patterns are always safer.

fallback

Fallback snip prefix when no rule matches:

  • null — no fallback (default)
  • { "prefix": "snip" } — use snip for all unmatched commands

llmModel

Override which model to use for LLM analysis. When omitted, uses OpenCode's default model.

{
  "providerID": "anthropic",
  "modelID": "claude-sonnet-4-20250514"
}

Rule Priority

Config rules (source: "config")
  ↓ no match
Learned rules (source: "learned")
  ↓ no match
Fallback prefix
  ↓ no match
No prefix added

Skipped Commands

The snip hook skips:

  • Shell builtins: cd, export, alias, source, echo, pwd, etc.
  • Already prefixed commands (no double-injection)
  • Environment variables are preserved: FOO=bar cmdFOO=bar snip cmd

Development

bun install
bun test
npm run typecheck

Testing & Linting

| Tool | What it checks | Script | |------|---------------|--------| | bun test | Unit tests in src/__tests__/ | bun test | | tsd | Type-level tests in src/*.test-d.ts | npm run tsd | | eslint with typescript-eslint | Code style and correctness | npm run lint (add -- --fix to auto-fix) | | knip | Dead code and unused exports | npm run knip | | publint | Package quality (exports, fields, compatibility) | npm run publint | | tsc -p tsconfig.build.json | TypeScript type checking | npm run typecheck |

Run all checks:

npm run typecheck && npm run lint && bun test && npm run tsd