Skip to content

feat: Add OpenCode plugin support#24

Merged
kodroi merged 7 commits intomainfrom
claude/add-opencode-support-yanFj
Feb 19, 2026
Merged

feat: Add OpenCode plugin support#24
kodroi merged 7 commits intomainfrom
claude/add-opencode-support-yanFj

Conversation

@kodroi
Copy link
Copy Markdown
Owner

@kodroi kodroi commented Feb 19, 2026

Add a TypeScript plugin for OpenCode that reuses the existing
protect_directories.py protection logic via tool.execute.before hook.
Intercepts edit, write, bash, and patch tools. +semver: minor

https://claude.ai/code/session_011KMGYhQjWHZKReC4TS4xa8

Summary by CodeRabbit

  • New Features

    • Added OpenCode integration with a Block plugin to enforce protection rules alongside Claude Code.
  • Documentation

    • Expanded docs and README with OpenCode installation, usage, local development, and architecture notes.
  • Tests

    • Added pytest as a dev dependency and a Testing section with local test commands.
  • Chores

    • Added package metadata for OpenCode, updated project description to include both platforms, and CI updates to support packaging/publishing.

Add a TypeScript plugin for OpenCode that reuses the existing
protect_directories.py protection logic via tool.execute.before hook.
Intercepts edit, write, bash, and patch tools. +semver: minor

https://claude.ai/code/session_011KMGYhQjWHZKReC4TS4xa8
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 19, 2026

Warning

Rate limit exceeded

@kodroi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 50 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds OpenCode integration: a new TypeScript BlockPlugin (opencode/index.ts) that calls the shared Python hook (hooks/protect_directories.py), package metadata (opencode/package.json), documentation updates (CLAUDE.md, README.md), CI changes (.github/workflows/ci.yml), and testing additions (pytest dev dependency, testing docs).

Changes

Cohort / File(s) Summary
Docs & Project Metadata
CLAUDE.md, README.md, pyproject.toml
Expanded docs and project description to include OpenCode alongside Claude Code; added OpenCode installation, usage, and local testing instructions; added pytest to dev dependencies.
OpenCode Integration
opencode/index.ts, opencode/package.json
New OpenCode BlockPlugin that filters specific tools, builds JSON hook input, invokes protect_directories.py via python3, parses JSON decisions and blocks or allows operations; package metadata and packaging scripts added.
CI Workflow
.github/workflows/ci.yml
CI updated to include opencode files in change filters, bump opencode/package.json version during tagging, commit version file, and publish opencode package to npm.
Shared Hook Reference
hooks/protect_directories.py
Referenced as the shared protection entrypoint invoked by both integrations (no functional changes to the script in this PR).

Sequence Diagram(s)

sequenceDiagram
    participant OC as OpenCode Agent
    participant Plugin as BlockPlugin
    participant Python as protect_directories.py
    participant FS as File System / Process

    OC->>Plugin: Invoke protected tool (edit/write/patch/bash)
    activate Plugin
    Plugin->>Plugin: Filter tool & build tool_input
    Plugin->>Plugin: Build JSON hook payload
    Plugin->>FS: Resolve Python script path
    Plugin->>Python: Execute `python3` with JSON on stdin
    activate Python
    Python->>Python: Parse input, evaluate rules
    Python-->>Plugin: Return JSON decision (allow/block + reason)
    deactivate Python

    alt decision == "block"
        Plugin-->>OC: Throw error with reason (block)
    else decision == "allow"
        Plugin-->>OC: Return null / allow operation
    end
    deactivate Plugin
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I nudged a new plugin into play,

Now two agents guard files each day,
Hooks and scripts in tidy rows,
I nibble code where safety grows,
Hooray — carrots safe for devs, hooray! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat: Add OpenCode plugin support' directly aligns with the main objective: adding a TypeScript plugin for OpenCode that integrates with the existing protection logic.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/add-opencode-support-yanFj

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (5)
opencode/index.ts (4)

11-11: dirname is imported but never used.

import.meta.dir is used directly on Line 68, making the dirname import dead code.

✏️ Proposed fix
-import { resolve, dirname } from "path";
+import { resolve } from "path";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opencode/index.ts` at line 11, The import list includes an unused symbol
"dirname" (import { resolve, dirname } from "path") while the code uses
import.meta.dir directly; remove "dirname" from the import to eliminate dead
code and keep only the needed "resolve" import in opencode/index.ts (update the
import statement to import { resolve } from "path").

67-70: import.meta.dir is a Bun-specific extension.

import.meta.dir is not part of the ECMAScript or WinterCG standards — it is a Bun proprietary addition. OpenCode uses Bun to install and run npm plugins, with packages cached in ~/.cache/opencode/node_modules/, so this works today. Document this runtime dependency assumption to prevent confusion if OpenCode ever changes its runtime or if someone tries to adapt the plugin elsewhere.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opencode/index.ts` around lines 67 - 70, The function findScript uses the
Bun-only extension import.meta.dir to locate protect_directories.py, which is a
nonstandard runtime assumption; update the codebase by adding a clear comment
above findScript documenting that import.meta.dir is Bun-specific and that
OpenCode expects packages under ~/.cache/opencode/node_modules/, add a short
runtime-check or warning (referencing findScript and import.meta.dir) that logs
a helpful message if import.meta.dir is unavailable, and add the same note to
the project README or plugin docs so future maintainers know the Bun dependency
and where packages are cached.

72-104: tool.execute.before does not fire for subagent tool calls — protection can be bypassed.

Plugin hooks using tool.execute.before successfully block tool calls from primary agents but do not intercept tool calls from subagents spawned via the task tool, allowing security policies to be completely bypassed. Users relying on this plugin for security-sensitive protection should be made aware of this limitation in the README.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opencode/index.ts` around lines 72 - 104, BlockPlugin's current hook on
"tool.execute.before" (in BlockPlugin) only catches primary-agent tool calls and
can be bypassed by subagents spawned via the task tool; update the code and
docs: add a clear README note describing this limitation and recommend users not
rely on BlockPlugin for subagent enforcement, and update the runtime enforcement
by either registering the same protection check for the subagent execution path
(e.g., also listen for the broader "tool.execute" or the task-spawn execution
event) or instrument the task/tool runner to call buildHookInput and the
BlockPlugin check before spawning subagents; reference BlockPlugin,
PROTECTED_TOOLS, buildHookInput and the "tool.execute.before" hook when making
the change so the protection logic is invoked for subagent tool calls as well.

43-52: Remove unused field name fallbacks or document compatibility intent.

The canonical field names are documented in the code comments: command for bash and file_path for file operations (lines 31–32). All test cases exclusively use these canonical names; no tests pass cmd, filePath, or file as alternatives. Either these fallbacks are dead code and should be removed, or they exist to handle backwards compatibility with Claude Code's original naming convention (referenced in the comment at line 18). Add a comment explaining the compatibility rationale if the fallbacks are intentional, or remove them if they serve no purpose.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opencode/index.ts` around lines 43 - 52, The code currently reads alternate
legacy field names (args.cmd, args.filePath, args.file) but tests and comments
use canonical names (command and file_path); remove the dead compatibility
fallbacks so the bash branch only reads args.command and the file branch only
reads args.file_path, and set toolInput.command = args.command and
toolInput.file_path = args.file_path directly (remove references to cmd,
filePath, file and their local variable aliases). If you intend to preserve
backward compatibility instead, add a short comment above these branches
explaining they intentionally accept legacy Claude Code names (cmd, filePath,
file) for compatibility; otherwise delete the fallback checks and local
variables.
opencode/package.json (1)

3-3: Version skew between packages is large and unexplained.

opencode/package.json declares 1.1.14 while pyproject.toml is at 1.1.6. These are separate packages so independent versioning is fine, but starting 8 versions ahead of the Python package may confuse users or downstream tooling that expects them to track each other.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opencode/package.json` at line 3, The package.json "version" field is eight
releases ahead of the Python package's version in pyproject.toml which will
confuse users; either sync the two versions or explicitly document independent
versioning. Fix by updating the "version" field in package.json to match the
pyproject.toml version (or vice‑versa), and update release notes/changelog and
any CI/release scripts that rely on matching versions; alternatively, add a
short note in the repo README explaining that the JS package ("version" in
package.json) and the Python package (version in pyproject.toml) use independent
versioning and why, so downstream tooling/users are not surprised.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@opencode/index.ts`:
- Around line 85-102: The try/catch around the shell invocation of python3 (the
$`echo ${hookInput} | python3 ${scriptPath}` call that assigns result and parses
stdout into decision) must gracefully handle infrastructure-level failures like
"python3 not found"; update the catch to detect these errors (e.g., err.code ===
'ENOENT' or err.message includes 'not found' / 'python3') and, instead of
re-throwing, log a warning and return so the operation proceeds, while
preserving the existing behavior of re-throwing actual block errors (when
decision.decision === "block") and other unexpected failures.
- Line 10: The import path for the Plugin type is wrong: replace the bare
specifier import type { Plugin } from "opencode/plugin" with the documented
package import from "@opencode-ai/plugin" so the TypeScript type resolution
matches the OpenCode API; update the import statement in opencode/index.ts to
import the Plugin type from "@opencode-ai/plugin" (referencing the Plugin
symbol) and run type-check to ensure no other import sites need the same change.

In `@opencode/package.json`:
- Around line 20-23: The package currently lists
"../hooks/protect_directories.py" in the package.json "files" array which npm
will drop; either copy hooks/protect_directories.py into the opencode/ package
directory and change the "files" entry to "protect_directories.py" and update
the findScript() in index.ts to resolve "./protect_directories.py", or add a
prepack/npm script that copies ../hooks/protect_directories.py into the package
dir at build time, keep the files entry pointing to the copied path, and ensure
findScript() in index.ts resolves the packaged path (not a parent-dir path) so
the file is present after npm publish.

In `@README.md`:
- Line 52: The README's manual copy instruction is incomplete: clarify that
opencode/index.ts depends on findScript() which resolves the Python file at
../hooks/protect_directories.py, so you must copy the entire opencode directory
tree preserving the sibling hooks/ directory (i.e., place hooks/ alongside
plugin/), not just opencode/index.ts; update the text to explicitly instruct
copying both the plugin/ folder and its sibling hooks/ folder (or provide the
full required directory layout) so the relative path used by findScript()
remains valid.

---

Nitpick comments:
In `@opencode/index.ts`:
- Line 11: The import list includes an unused symbol "dirname" (import {
resolve, dirname } from "path") while the code uses import.meta.dir directly;
remove "dirname" from the import to eliminate dead code and keep only the needed
"resolve" import in opencode/index.ts (update the import statement to import {
resolve } from "path").
- Around line 67-70: The function findScript uses the Bun-only extension
import.meta.dir to locate protect_directories.py, which is a nonstandard runtime
assumption; update the codebase by adding a clear comment above findScript
documenting that import.meta.dir is Bun-specific and that OpenCode expects
packages under ~/.cache/opencode/node_modules/, add a short runtime-check or
warning (referencing findScript and import.meta.dir) that logs a helpful message
if import.meta.dir is unavailable, and add the same note to the project README
or plugin docs so future maintainers know the Bun dependency and where packages
are cached.
- Around line 72-104: BlockPlugin's current hook on "tool.execute.before" (in
BlockPlugin) only catches primary-agent tool calls and can be bypassed by
subagents spawned via the task tool; update the code and docs: add a clear
README note describing this limitation and recommend users not rely on
BlockPlugin for subagent enforcement, and update the runtime enforcement by
either registering the same protection check for the subagent execution path
(e.g., also listen for the broader "tool.execute" or the task-spawn execution
event) or instrument the task/tool runner to call buildHookInput and the
BlockPlugin check before spawning subagents; reference BlockPlugin,
PROTECTED_TOOLS, buildHookInput and the "tool.execute.before" hook when making
the change so the protection logic is invoked for subagent tool calls as well.
- Around line 43-52: The code currently reads alternate legacy field names
(args.cmd, args.filePath, args.file) but tests and comments use canonical names
(command and file_path); remove the dead compatibility fallbacks so the bash
branch only reads args.command and the file branch only reads args.file_path,
and set toolInput.command = args.command and toolInput.file_path =
args.file_path directly (remove references to cmd, filePath, file and their
local variable aliases). If you intend to preserve backward compatibility
instead, add a short comment above these branches explaining they intentionally
accept legacy Claude Code names (cmd, filePath, file) for compatibility;
otherwise delete the fallback checks and local variables.

In `@opencode/package.json`:
- Line 3: The package.json "version" field is eight releases ahead of the Python
package's version in pyproject.toml which will confuse users; either sync the
two versions or explicitly document independent versioning. Fix by updating the
"version" field in package.json to match the pyproject.toml version (or
vice‑versa), and update release notes/changelog and any CI/release scripts that
rely on matching versions; alternatively, add a short note in the repo README
explaining that the JS package ("version" in package.json) and the Python
package (version in pyproject.toml) use independent versioning and why, so
downstream tooling/users are not surprised.

Comment thread opencode/index.ts Outdated
Comment thread opencode/index.ts
Comment thread opencode/package.json
Comment thread README.md Outdated
- Fix import path: opencode/plugin → @opencode-ai/plugin
- Remove unused dirname import
- Handle python3-not-found gracefully (warn and allow operation)
- Add prepack script to copy protect_directories.py for npm publish
- Use canonical OpenCode arg names (filePath, command) without fallbacks
- Add Bun-specific comment on import.meta.dir
- Clarify README manual setup with required directory layout
- Document subagent limitation for tool.execute.before hook

https://claude.ai/code/session_011KMGYhQjWHZKReC4TS4xa8
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@opencode/package.json`:
- Around line 7-8: The prepack/postpack npm scripts use POSIX-only commands
(`cp` and `rm -f`) which fail on Windows; update the package.json scripts
`prepack` and `postpack` to use cross-platform `shx` equivalents (e.g., `shx cp
../hooks/protect_directories.py protect_directories.py` and `shx rm -f
protect_directories.py`) and add `shx` as a devDependency so the commands are
available across platforms; ensure the referenced file name
`protect_directories.py` remains the same.
- Around line 1-28: The package.json is missing a dependencies section for the
runtime import used in index.ts; add a "dependencies" object to package.json and
declare "@opencode-ai/plugin" with an appropriate semver (e.g., a caret-range
matching the plugin API you built against) so that when Bun/npm installs this
package it also installs `@opencode-ai/plugin`; update the "files" or other
metadata only if necessary, but the main fix is adding the dependencies entry
referencing "@opencode-ai/plugin" so the import in index.ts resolves at runtime.

In `@README.md`:
- Line 54: The README's fenced code block containing the directory-tree snippet
(the block showing "your-project/") lacks a language specifier; update that
fence to include a language tag such as text or plaintext (e.g., change the
opening ``` to ```text) so the directory-tree code block satisfies MD040 and
renders as plain text.
- Around line 38-42: The README shows the opencode.json key as "plugins"
(plural) but OpenCode expects "plugin" (singular); update both occurrences of
the key in the README examples so the JSON uses "plugin" and keeps the array
value intact (i.e., replace "plugins" with "plugin" in the opencode.json sample
blocks and any other examples referencing that key).

---

Duplicate comments:
In `@opencode/package.json`:
- Around line 24-27: The package.json "files" field currently lists "index.ts"
and "protect_directories.py", but to ensure the packaged artifact is correct you
must keep "protect_directories.py" as the local copy created by the prepack
script and ensure the prepack/postpack scripts actually perform the copy and
cleanup; specifically verify the prepack script copies
../hooks/protect_directories.py into the package root before packing and the
postpack script removes that local copy afterward so the "files" field (in
package.json) correctly references the prepack-copied protect_directories.py and
index.ts.

Comment thread opencode/package.json
Comment on lines +1 to +28
{
"name": "opencode-block",
"version": "1.1.14",
"description": "File and directory protection for OpenCode using .block marker files with pattern matching",
"main": "index.ts",
"scripts": {
"prepack": "cp ../hooks/protect_directories.py protect_directories.py",
"postpack": "rm -f protect_directories.py"
},
"keywords": [
"opencode",
"opencode-plugin",
"protection",
"security",
"file-blocking",
"directory-lock"
],
"author": "Iiro Rahkonen",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/kodroi/block"
},
"files": [
"index.ts",
"protect_directories.py"
]
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's verify the package.json location and content
find . -name "package.json" -path "*/opencode/*" | head -20

Repository: kodroi/block

Length of output: 79


🏁 Script executed:

# Also, let's look for the index.ts file
find . -name "index.ts" -path "*/opencode/*" | head -20

Repository: kodroi/block

Length of output: 75


🏁 Script executed:

# Check the repository structure to understand the layout
git ls-files | grep -E "(opencode|package\.json|index\.ts)" | head -30

Repository: kodroi/block

Length of output: 95


🏁 Script executed:

# Read the index.ts file to verify imports
cat -n ./opencode/index.ts

Repository: kodroi/block

Length of output: 5285


🏁 Script executed:

# Read package.json to verify its current state
cat -n ./opencode/package.json

Repository: kodroi/block

Length of output: 916


🏁 Script executed:

# Search for any OpenCode documentation or examples about plugin dependencies
find . -type f -name "*.md" -o -name "*.txt" | xargs grep -l "opencode-ai/plugin\|plugin.*dependency" 2>/dev/null | head -10

Repository: kodroi/block

Length of output: 38


🏁 Script executed:

# Check if there are other plugin examples in the repository
find . -type f -name "package.json" | xargs grep -l "@opencode-ai/plugin\|opencode-plugin" | head -10

Repository: kodroi/block

Length of output: 79


🏁 Script executed:

# Look for any tsconfig or build configuration that might indicate how plugins are compiled
find . -name "tsconfig.json" -o -name "bunfig.toml" -o -name ".bun*" | head -10

Repository: kodroi/block

Length of output: 38


🏁 Script executed:

# Check if `@opencode-ai/plugin` is used elsewhere in the repo
rg "@opencode-ai/plugin" --type ts --type json

Repository: kodroi/block

Length of output: 124


🌐 Web query:

OpenCode plugin dependencies @opencode-ai/plugin how to declare

💡 Result:

To declare dependencies for an OpenCode plugin (using @opencode-ai/plugin), you do it via package.json—where you put that package.json depends on whether your plugin is local or an npm package.

1) Local plugin (.opencode/plugins/*)

Create a package.json in your OpenCode config directory (commonly .opencode/package.json in the project), and list the npm deps you want your plugin to import:

{
  "dependencies": {
    "@opencode-ai/plugin": "^1.1.34",
    "zod": "^4.1.8"
  }
}

OpenCode will run bun install at startup, and your plugin can then import those packages. (opencode.ai)

Example TypeScript usage:

import type { Plugin } from "@opencode-ai/plugin"

(opencode.ai)

2) Plugin published to npm (loaded via opencode.json)

You publish your plugin as a normal npm package with its own package.json dependencies, then load it in opencode.json:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["@my-org/custom-plugin"]
}

(opencode.ai)

Citations:


🌐 Web query:

OpenCode npm plugin installation Bun dependency resolution

💡 Result:

Installing OpenCode plugins from npm

  1. Add the package name(s) to plugin in your opencode.json:
    {
      "$schema": "https://opencode.ai/config.json",
      "plugin": ["opencode-helicone-session", "opencode-wakatime", "@my-org/custom-plugin"]
    }
  2. Restart OpenCode. It installs npm plugins automatically using Bun at startup and caches them under ~/.cache/opencode/node_modules/. [1]

If you’re writing a local plugin and need dependencies

  1. Put your plugin in .opencode/plugins/ (project) or ~/.config/opencode/plugins/ (global). [1]
  2. Add a package.json in the OpenCode config directory (commonly .opencode/package.json) with your dependencies, e.g.:
    { "dependencies": { "shescape": "^2.1.0" } }
    OpenCode will run bun install at startup so your plugin can import them. [1]

Bun dependency resolution knobs (when something won’t resolve / wrong transitive version)

  • Use overrides (npm) or resolutions (Yarn-style) in that same package.json to force transitive (“metadependency”) versions; Bun supports both. [2][3]
    {
      "dependencies": { "your-dep": "^1" },
      "overrides": { "problem-transitive": "1.2.3" }
    }

If installs fail due to lifecycle scripts (postinstall/native builds)

  • Bun may block lifecycle scripts by default; allow them by adding the package to trustedDependencies (or run bun pm trust <pkg>), then reinstall. [4]

Sources: OpenCode plugin docs [1]; Bun overrides/resolutions docs [2][3]; Bun trusted dependencies docs [4].


🏁 Script executed:

# Check the README for any setup or installation instructions
cat -n README.md 2>/dev/null || echo "No README.md found"

Repository: kodroi/block

Length of output: 8663


🏁 Script executed:

# Look for any GitHub workflow or CI configuration that might show how plugins are tested
find . -type f \( -name "*.yml" -o -name "*.yaml" \) | head -5 | xargs cat 2>/dev/null

Repository: kodroi/block

Length of output: 17569


Add missing @opencode-ai/plugin dependency declaration.

The package has no dependencies field. index.ts imports from @opencode-ai/plugin (line 10), but it isn't declared in package.json. When Bun installs this plugin from npm, it won't install @opencode-ai/plugin alongside it, causing a Cannot find module "@opencode-ai/plugin" error at runtime.

For npm-published plugins, dependencies must be declared in the plugin's own package.json. Add:

📦 Proposed fix
+  "dependencies": {
+    "@opencode-ai/plugin": "latest"
+  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"name": "opencode-block",
"version": "1.1.14",
"description": "File and directory protection for OpenCode using .block marker files with pattern matching",
"main": "index.ts",
"scripts": {
"prepack": "cp ../hooks/protect_directories.py protect_directories.py",
"postpack": "rm -f protect_directories.py"
},
"keywords": [
"opencode",
"opencode-plugin",
"protection",
"security",
"file-blocking",
"directory-lock"
],
"author": "Iiro Rahkonen",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/kodroi/block"
},
"files": [
"index.ts",
"protect_directories.py"
]
}
{
"name": "opencode-block",
"version": "1.1.14",
"description": "File and directory protection for OpenCode using .block marker files with pattern matching",
"main": "index.ts",
"scripts": {
"prepack": "cp ../hooks/protect_directories.py protect_directories.py",
"postpack": "rm -f protect_directories.py"
},
"dependencies": {
"@opencode-ai/plugin": "latest"
},
"keywords": [
"opencode",
"opencode-plugin",
"protection",
"security",
"file-blocking",
"directory-lock"
],
"author": "Iiro Rahkonen",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/kodroi/block"
},
"files": [
"index.ts",
"protect_directories.py"
]
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@opencode/package.json` around lines 1 - 28, The package.json is missing a
dependencies section for the runtime import used in index.ts; add a
"dependencies" object to package.json and declare "@opencode-ai/plugin" with an
appropriate semver (e.g., a caret-range matching the plugin API you built
against) so that when Bun/npm installs this package it also installs
`@opencode-ai/plugin`; update the "files" or other metadata only if necessary, but
the main fix is adding the dependencies entry referencing "@opencode-ai/plugin"
so the import in index.ts resolves at runtime.

Comment thread opencode/package.json Outdated
Comment thread README.md
Comment on lines +38 to +42
```json
{
"plugins": ["opencode-block"]
}
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

OpenCode configuration schema opencode.json plugin key

💡 Result:

In OpenCode’s opencode.json / opencode.jsonc config, the key for plugins is:

  • "plugin": an array of plugin identifiers (npm package names, including scoped packages; you can also pin versions like name@x.y.z). (opencode.ai)

Example:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["opencode-helicone-session", "opencode-wakatime", "@my-org/custom-plugin"]
}

(opencode.ai)

Schema URL (for editor validation/autocomplete):

  • "$schema": "https://opencode.ai/config.json" (opencode.ai)

Citations:


🏁 Script executed:

grep -n "plugins" README.md | head -20

Repository: kodroi/block

Length of output: 150


opencode.json config key should be "plugin" (singular), not "plugins" (plural).

OpenCode's configuration schema uses "plugin" for the plugins array. The README incorrectly shows "plugins" (plural) in two places (lines 40 and 48), which won't be recognized and the plugin won't load.

Proposed fixes

Line 40:

 {
-  "plugins": ["opencode-block"]
+  "plugin": ["opencode-block"]
 }

Line 48:

 {
-  "plugins": ["file:///path/to/block/opencode/index.ts"]
+  "plugin": ["file:///path/to/block/opencode/index.ts"]
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 38 - 42, The README shows the opencode.json key as
"plugins" (plural) but OpenCode expects "plugin" (singular); update both
occurrences of the key in the README examples so the JSON uses "plugin" and
keeps the array value intact (i.e., replace "plugins" with "plugin" in the
opencode.json sample blocks and any other examples referencing that key).

Comment thread README.md

You can also set up the plugin manually by copying files into your project. The plugin expects `hooks/protect_directories.py` to be a sibling of the directory containing `index.ts`:

```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a language specifier to the fenced code block (MD040).

The directory-tree code block on line 54 is missing a language tag; use text or plaintext.

📝 Proposed fix
-```
+```text
 your-project/
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 54-54: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 54, The README's fenced code block containing the
directory-tree snippet (the block showing "your-project/") lacks a language
specifier; update that fence to include a language tag such as text or plaintext
(e.g., change the opening ``` to ```text) so the directory-tree code block
satisfies MD040 and renders as plain text.

Add publish-npm job to CI that publishes to npm after tagging.
Sync opencode/package.json version in the tag job and include
opencode/** in the versionable paths filter.

Requires NPM_TOKEN repository secret.

https://claude.ai/code/session_011KMGYhQjWHZKReC4TS4xa8
Replace long-lived NPM_TOKEN secret with GitHub Actions OIDC
id-token permission for secure, tokenless npm publishing.
Provenance attestations are generated automatically.

https://claude.ai/code/session_011KMGYhQjWHZKReC4TS4xa8
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 548-551: The "Update package version" step in the publish-npm job
runs npm version with the same version already committed and causes "Version not
changed" failures; remove that step entirely (the tag job already updated and
pushed opencode/package.json) or, if you need a local-only bump, change the
operation to either run npm version with --allow-same-version or read the
version from package.json (e.g., with jq) and skip npm version. Update the
publish-npm job to rely on the checked-out main's package.json instead of
re-running npm version.
- Around line 527-556: The publish-npm job currently uses Node 20 which ships
npm 10.x and will fail Trusted Publishing OIDC; before publishing update npm to
at least 11.5.1 (e.g., run npm install -g npm@^11.5.1 or npm@latest in the Setup
Node.js step), run npm ci inside opencode to install/build so prepack/prepublish
scripts run (add an Install/Build step before Update package version or before
Publish to npm), and add the provenance flag to the publish command (use npm
publish --access public --provenance or set NPM_CONFIG_PROVENANCE=true) in the
Publish to npm step; also ensure the first-time publish uses a traditional npm
token (Trusted Publisher must be configured afterward on npmjs.com) so the OIDC
flow succeeds later.

Comment thread .github/workflows/ci.yml Outdated
Comment on lines +527 to +556
publish-npm:
name: Publish to npm
runs-on: ubuntu-latest
needs: [version, tag]
if: github.ref == 'refs/heads/main'
permissions:
contents: read
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
ref: main

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'

- name: Update package version
run: |
cd opencode
npm version ${{ needs.version.outputs.majorMinorPatch }} --no-git-tag-version

- name: Publish to npm
run: |
cd opencode
npm publish --access public
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

fd -g 'package.json' opencode/ --exec cat {}

Repository: kodroi/block

Length of output: 720


🌐 Web query:

npm version bundled with Node.js 20 LTS

💡 Result:

For the current Node.js 20 LTS (20.20.0, released Jan 12, 2026), the bundled npm version is npm 10.8.2. [1][2]

Note: The bundled npm version can vary across Node 20.x releases; the Node.js v20 download archive lists the npm version per release. [1]

Sources: [1] [2]


🌐 Web query:

npm trusted publishing OIDC registry-url conflict Node.js actions/setup-node

💡 Result:

When you set registry-url in actions/setup-node, it writes a project-local .npmrc (including an auth line like //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}), which can override/conflict with other npm-auth flows in the same job (notably tools like semantic-release) and can also “stick” if you switch registries multiple times in one workflow/job. [2], [3]

What to do (depending on your setup)

  1. If you’re using npm Trusted Publishing (OIDC) + semantic-release: don’t set registry-url
    semantic-release explicitly warns that registry-url can cause .npmrc conflicts and EINVALIDNPMTOKEN. Configure registry via your repo .npmrc (if needed) and let semantic-release handle auth. [3]

  2. If you’re using npm Trusted Publishing (OIDC) without semantic-release: keep registry-url on npm’s registry and don’t provide a publish token
    npm’s own Trusted Publishing example uses registry-url: 'https://registry.npmjs.org' and then runs npm publish with id-token: write permissions (no NODE_AUTH_TOKEN for publish). [1]

  3. If you must use a token for installs (private deps), scope it to the install step only
    Trusted Publishing only covers npm publish; installs may still need a (read-only) token. Use NODE_AUTH_TOKEN only on npm ci, not globally. [1]

permissions:
  contents: read
  id-token: write

steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with:
      node-version: 24
      registry-url: https://registry.npmjs.org

  - run: npm ci
    env:
      NODE_AUTH_TOKEN: ${{ secrets.NPM_READ_TOKEN }} # install only

  - run: npm publish # OIDC is used here (no NODE_AUTH_TOKEN)
  1. If your issue is “registry switching” within one job
    There are known cases where changing registry-url across multiple setup-node invocations doesn’t take effect as expected; a practical workaround is to avoid switching registries in the same job (split into separate jobs) or explicitly manage the .npmrc you want for each publish. [4]

Sources: npm Trusted Publishing docs [1]; GitHub docs on setup-node creating .npmrc from registry-url/NODE_AUTH_TOKEN [2]; semantic-release guidance to avoid registry-url [3]; setup-node registry switching issue [4].


🌐 Web query:

npm CLI version requirement trusted publishing npm 11.5.1

💡 Result:

npm Trusted Publishing (OIDC) requires:

  • npm CLI: v11.5.1 or later
  • Node.js: v22.14.0 or later [1]

To verify your CLI version:

npm -v

To upgrade npm (example):

npm i -g npm@^11.5.1

Sources: npm Trusted Publishing docs [1].


publish-npm job has one critical issue and several gaps to address

1. npm CLI version is below the Trusted Publishing requirement (critical)

Trusted Publishing requires npm CLI v11.5.1 or later, but Node.js 20 bundles npm 10.8.2. Add npm install -g npm@latest (or pin to ≥11.5.1) before the publish step. Without this upgrade, the OIDC token exchange will fail.

2. npm ci before publish is recommended practice

The workflow lacks npm ci before npm publish. While opencode/package.json has no prepare or prepublishOnly scripts, running npm ci ensures prepack lifecycle scripts execute reliably (this package copies files via prepack). Add a build/install step for robustness.

3. Consider adding --provenance explicitly

Even though Trusted Publishing doesn't strictly require --provenance, several users report that adding the flag made the initial publish succeed and produces clearer error messages. Add npm publish --access public --provenance or set NPM_CONFIG_PROVENANCE=true.

4. Trusted Publisher must be pre-configured on npmjs.com

npm requires the package to exist before a Trusted Publisher can be configured; the first publish must use a traditional token. After that initial publish, OIDC can be enabled. Ensure this setup is complete before merging.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 527 - 556, The publish-npm job
currently uses Node 20 which ships npm 10.x and will fail Trusted Publishing
OIDC; before publishing update npm to at least 11.5.1 (e.g., run npm install -g
npm@^11.5.1 or npm@latest in the Setup Node.js step), run npm ci inside opencode
to install/build so prepack/prepublish scripts run (add an Install/Build step
before Update package version or before Publish to npm), and add the provenance
flag to the publish command (use npm publish --access public --provenance or set
NPM_CONFIG_PROVENANCE=true) in the Publish to npm step; also ensure the
first-time publish uses a traditional npm token (Trusted Publisher must be
configured afterward on npmjs.com) so the OIDC flow succeeds later.

Comment thread .github/workflows/ci.yml Outdated
Copy README.md from repo root during prepack so it appears on the
npm package page.

https://claude.ai/code/session_011KMGYhQjWHZKReC4TS4xa8
- Remove redundant npm version step (tag job already updates package.json)
- Upgrade npm to 11.5+ (Node 20's npm 10.x lacks OIDC support)
- Add --provenance flag for supply chain attestations

https://claude.ai/code/session_011KMGYhQjWHZKReC4TS4xa8
@kodroi kodroi merged commit f18b537 into main Feb 19, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants