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-snippets

v2.2.2

Published

Hashtag-based snippet expansion plugin for OpenCode - instant inline text shortcuts

Readme

opencode-snippets

Instant inline text expansion for OpenCode - Type #snippet anywhere in your message and watch it transform.

[!TIP] Share Your Snippets!
Got a snippet that saves you time? Share yours or steal ideas from the community! Browse and contribute in GitHub Discussions.

Why Snippets?

As developers, we DRY (Don't Repeat Yourself) our code. We extract functions, create libraries, compose modules. Why should our prompts be any different?

Stop copy-pasting (or worse, typing 🤢) the same instructions into every message. Snippets bring software engineering principles to prompt engineering:

  • 🔄 DRY - Write once, reuse everywhere
  • 🧩 Composability - Build complex prompts from simple pieces
  • 🔧 Maintainability - Update once, apply everywhere
  • 🔍 Discoverability - Your team's best practices, always a #hashtag away

OpenCode's /slash commands must come first. Snippets work anywhere:

# Slash commands (must be first):
/git-status Please review my changes

# Snippets (anywhere!):
Please review my changes #git-status and suggest improvements #code-style

Snippets work like @file mentions - natural, inline, composable.

🎯 Composable by Design

Snippets compose with each other and with slash commands. Reference #snippets anywhere - in your messages, in slash commands, even inside other snippets:

Example: Extending commands with snippets

~/.config/opencode/command/commit-and-push.md:

---
description: Create a git commit and push to remote
---
Please create a git commit with the current changes and push to the remote repository.
#use-conventional-commits

Here is the current git status:
!`git status`

Here are the staged changes:
!`git diff --cached`

#project-context

You could also make "current git status and staged changes" a shell-enabled snippet of its own.

Example: Snippets composing snippets

~/.config/opencode/snippet/code-standards.md:

#style-guide
#error-handling
#testing-requirements

https://github.com/user-attachments/assets/76975a9e-e326-431e-8be5-39a9f6572851

~/.config/opencode/snippet/full-review.md:

#code-standards
#security-checklist
#performance-tips

Compose base snippets into higher-level ones. Type #full-review to inject all standards at once, keeping each concern in its own maintainable file.

The power: Mix and match. Type #tdd #careful for test-driven development with extra caution. Build /commit #conventional-commits #project-context for context-aware commits. Create layered prompts from small, reusable pieces.

Installation

opencode plugin opencode-snippets -gf

This installs the package and wires up both the server plugin and the TUI plugin for autocompletion automatically.

If you edit config manually, the configurations are separate, so you need both entries yourself.

Required: add the package to your opencode.json plugins array:

{
  "plugins": [
    "opencode-snippets"
  ]
}

Strongly recommended: add the same package to tui.json too:

{
  "plugin": [
    "opencode-snippets"
  ]
}

For local development with a file:/// plugin path, point OpenCode at the package directory:

{
  "plugins": [
    "file:///D:/projects/opencode-snippets"
  ]
}

Strongly recommended for local TUI testing too, wire the same package directory into tui.json:

{
  "plugin": [
    "file:///D:/projects/opencode-snippets"
  ]
}

Using the directory lets OpenCode read the package manifest and discover both targets.

Quick Start

1. Create your global snippets directory:

mkdir -p ~/.config/opencode/snippet

The plugin also loads ~/.config/opencode/snippets/ if you already use the plural form.

2. Add your first snippet:

~/.config/opencode/snippet/careful.md:

---
aliases: safe
---
Think step by step. Double-check your work before committing changes.
Ask clarifying questions if anything is ambiguous.

3. Use it anywhere:

https://github.com/user-attachments/assets/ebb303b5-d41b-4d87-8f08-eb1d730db5c8

Where to Store Snippets

Snippets can be global (~/.config/opencode/snippet/*.md or ~/.config/opencode/snippets/*.md) or project-specific (.opencode/snippet/*.md or .opencode/snippets/*.md). Both singular and plural directory names are loaded automatically. Project snippets override global ones with the same name, and snippet/ wins over snippets/ within the same scope.

Features

Aliases

Define multiple triggers for the same snippet:

~/.config/opencode/snippet/cherry-pick.md:

---
aliases:
  - cp
  - pick
description: "Git cherry-pick helper"
---
Always pick parent 1 for merge commits.

Now #cherry-pick, #cp, and #pick all expand to the same content.

Single alias doesn't need array syntax:

---
aliases: safe
---

You can also use JSON array style: aliases: ["cp", "pick"]

Shell Command Substitution

The plugin adds shell substitution to regular OpenCode prompts, not just snippet files. Use !`command` for output-only injection and !>`command` when you want the executed command shown too:

Current branch: !`git branch --show-current`
Last commit: !`git log -1 --oneline`
Working directory: !`pwd`
Debug listing: !>`ls`

Default: !`ls` injects only command output, matching OpenCode command templates.

Verbose form: !>`ls`

$ ls
--> <output>

LLMs tend to trust the output more when they can see which terminal command just ran. The command gives the output context, which makes it more informative and easier to interpret.

Recursive Includes

Snippets can include other snippets using #snippet-name syntax. This allows building complex, composable snippets from smaller pieces:

# In base-style.md:
Use TypeScript strict mode. Always add JSDoc comments.

# In python-style.md:
Use type hints. Follow PEP 8.

# In review.md:
Review this code carefully:
#base-style
#python-style
#security-checklist

Loop Protection: Snippets are expanded up to 15 times per message to support deep nesting. If a circular reference is detected (e.g., #a includes #b which includes #a), expansion stops after 15 iterations and the remaining hashtag is left as-is. A warning is logged to help debug the issue.

Example of loop protection:

# self.md contains: "I reference #self"
# Expanding #self produces:
I reference I reference I reference ... (15 times) ... I reference #self

This generous limit supports complex snippet hierarchies while preventing infinite loops.

Prepend and Append Blocks

For long reference material that would break your writing flow, use <append> blocks to place content at the end of your message:

---
aliases: jira-mcp
---
Jira MCP server
<append>
## Jira MCP Usage

Use these custom field mappings when creating issues:
- customfield_16570 => Acceptance Criteria
- customfield_11401 => Team
</append>

Input: Create a bug ticket in #jira-mcp about the memory leak

Output:

Create a bug ticket in Jira MCP server about the memory leak

## Jira MCP Usage

Use these custom field mappings when creating issues:
- customfield_16570 => Acceptance Criteria
- customfield_11401 => Team

Write naturally—reference what you need mid-sentence—and the context follows at the bottom.

Use <prepend> for content that should appear at the top of your message. Multiple blocks of the same type are concatenated in order of appearance.

Block behavior:

  • Content outside <prepend>/<append> blocks replaces the hashtag inline
  • If a snippet has only blocks (no inline content), the hashtag is simply removed
  • Blocks from nested snippets are collected and assembled in the final message
  • Unclosed tags are handled leniently (rest of content becomes the block)
  • Nested blocks are not allowed—the hashtag is left unchanged

Inject Blocks (Experimental)

Add persistent context that the LLM sees throughout the entire agentic loop, without cluttering your visible message:

---
aliases: safe
---
Think step by step.
<inject>
IMPORTANT: Double-check all code for security vulnerabilities.
Always suggest tests for any implementation.
</inject>

Input: Review this code #safe

What happens:

  • Your message shows: Review this code Think step by step.
  • The LLM also receives the inject content as a separate context message
  • This context persists for the entire conversation turn (agentic loop)

Use inject blocks for rules, constraints, or instructions that should influence all LLM responses without appearing inline in your message.

Injected context is placed N messages from the bottom of the conversation (default: 5) to prevent instruction overfitting, where the model fixates on injected content as if it were the user's latest directive. As the conversation grows, the injection floats upward, maintaining a steady distance from the latest turn. Configure the offset with injectRecencyMessages. For the full design rationale, see Injection Placement Strategy.

Enable in config:

{
  "experimental": {
    "injectBlocks": true
  }
}

Skill Rendering (Experimental)

Inline OpenCode skills directly into your messages using XML-style tags:

Create a Jira ticket. <skill>jira</skill>

Or use the self-closing format:

<skill name="jira" /> Create a ticket for the bug.

Enable in config:

{
  "experimental": {
    "skillRendering": true
  }
}

Skills are loaded from OpenCode's standard skill directories:

  • Global: ~/.config/opencode/skill/<name>/SKILL.md
  • Project: .opencode/skill/<name>/SKILL.md

When a skill tag is found, it's replaced with the skill's content body (frontmatter stripped). Unknown skills leave the tag unchanged.

Skill Loading (Experimental)

Load a skill with OpenCode-style wrapper content without showing the full skill body inline:

Write this in caveman mode. #skill(caveman)

Quoted names are also supported:

#skill("opencode-config")

Enable in config:

{
  "experimental": {
    "skillLoading": true
  }
}

When enabled, the user-visible message shows ↳ Loaded name, while the model receives an injected OpenCode-style <skill_content> payload immediately after that message. Multiple #skill(...) calls in one message are injected in source order.

Quick project-local demo in this repo:

Explain closures in two lines. #skill(demo-voice)

Or use the included snippet that expands into #skill(...):

#demo-skill Explain closures in two lines.

Demo files live at .opencode/skill/demo-voice/SKILL.md and .opencode/snippet/demo-skill.md.

Commands

  • /snippets add <name> [content] creates a global snippet
  • /snippets add --project <name> creates a project snippet
  • /snippets list shows available snippets
  • /snippets delete <name> removes a snippet
  • /snippets:reload reloads snippet files from disk without restarting OpenCode

Example Snippets

~/.config/opencode/snippet/context.md

---
aliases: ctx
---
Project: !`basename $(pwd)`
Branch: !`git branch --show-current`
Recent changes: !`git diff --stat HEAD~3 | tail -5`

~/.config/opencode/snippet/minimal.md

---
aliases:
  - min
  - terse
---
Be extremely concise. No explanations unless asked.

Snippets vs Slash Commands

| Feature | /commands | #snippets | |---------|-------------|-------------| | Position | Must come first 🏁 | Anywhere 📍 | | Multiple per message | No ❌ | Yes ✅ | | Live shell data | Yes 💻 | Yes 💻 | | Best for | Triggering actions & workflows ⚡ | Context injection 📝 |

[!TIP]

My recommendation:

Use /slash commands for triggering actions and workflows imperatively - anything that needs to happen right now: /commit-and-push, /add-worktree, or /pull-rebase.
Use #snippets for all other context engineering.

If you can't decide, get the best of both worlds and just have your command proxy through to the snippet:

~/.config/opencode/command/pull.md:

---
description: Proxy through to the snippet at snippet/pull.md
---
#pull

Configuration

The plugin can be configured via config.jsonc files:

  • Global: ~/.config/opencode/snippet/config.jsonc
  • Project: .opencode/snippet/config.jsonc (overrides global settings)

Snippet markdown files are loaded from both snippet/ and snippets/, but config files stay in snippet/config.jsonc.

A default config file is created automatically on first run.

Full Configuration Example

{
  "$schema": "https://raw.githubusercontent.com/JosXa/opencode-snippets/v2.2.2/schema/config.schema.json",
  "logging": {
    "debug": false // Enable debug logging (logs: ~/.config/opencode/logs/snippets/daily/)
  },
  "experimental": {
    "injectBlocks": false, // Enable <inject>...</inject> blocks for persistent context
    "skillRendering": false, // Enable <skill>name</skill> tag expansion
    "skillLoading": false // Enable #skill(name) OpenCode-style loading
  },
  "injectRecencyMessages": 5 // How many messages from the bottom to place injected context
}

All boolean settings accept: true, false, "enabled", "disabled"

Debug Logging

Logs are written to ~/.config/opencode/logs/snippets/daily/ when enabled.

Behavior Notes

  • Snippets expand everywhere: regular chat, question responses, skills, and slash commands
  • Injected snippet context is placed N messages from the bottom (configured by injectRecencyMessages) and shows a ↳ Injected #name indicator when first registered
  • #skill(name) inserts OpenCode-style skill payload text above the visible user message while keeping the transcript inline placeholder compact
  • Snippets are loaded once at plugin startup
  • Hashtag matching is case-insensitive (#Hello = #hello)
  • Unknown hashtags are left unchanged
  • !`cmd` injects output only, while !>`cmd` injects $ cmd plus output
  • Failed shell commands preserve the original syntax in output
  • Frontmatter is stripped from expanded content
  • Only user messages are processed (not assistant responses)

Contributing

Contributions welcome! Please open an issue or PR on GitHub. 👥 Discord Forum

License

MIT