feat: add OpenCode support - #746
Conversation
📝 WalkthroughWalkthroughThe PR adds generated OpenCode support, including skills, agents, commands, plugins, artifact scripts, CI and release integration, installation, documentation, research evaluation tooling, and Bats coverage. ChangesOpenCode support
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Marketplace
participant Generator
participant OpenCodeBundle
participant CI
participant Release
Marketplace->>Generator: provide eligible plugin metadata
Generator->>OpenCodeBundle: generate skills, agents, commands, and plugin
CI->>Generator: synchronize and check artifacts
CI->>OpenCodeBundle: verify tracked and untracked files
Release->>Generator: synchronize release artifacts
Release->>OpenCodeBundle: include .opencode assets
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 17
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.opencode/skills/ship/SKILL.md (1)
1-104: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winRegenerate the stale artifact before merge.
CI reports that
.opencode/skills/ship/SKILL.mddiffers from the expected generated output, so the artifact check fails. Updateplugins/me/skills/ship/SKILL.md, runbun run sync:opencode, then runbun run check:opencode, and commit the generated result. Do not edit generated.opencodecontent directly.As per coding guidelines, committed
.opencode/bundles must be checked withbun run check:opencode.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/skills/ship/SKILL.md around lines 1 - 104, Regenerate the stale ship skill artifact by updating the source content in plugins/me/skills/ship/SKILL.md rather than editing .opencode/skills/ship/SKILL.md directly. Run bun run sync:opencode, verify the generated bundle with bun run check:opencode, and commit the resulting generated changes.Sources: Coding guidelines, Pipeline failures
🟡 Minor comments (10)
CLAUDE.md-58-59 (1)
58-59: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winDocument every OpenCode source that requires regeneration.
The generator copies skills and converts agent and command files. Metadata-only guidance can leave
.opencode/stale after source content changes.
CLAUDE.md#L58-L59: Requirebun run sync:opencodeafter changes to marketplace metadata, skills, agents, commands, or the plugin template.README.md#L38-L43: List those source directories and files with the marketplace metadata as OpenCode generation inputs.As per coding guidelines,
.opencode/**requiresbun run check:opencodeto verify that the committed bundle is in sync.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@CLAUDE.md` around lines 58 - 59, Update CLAUDE.md lines 58-59 to require bun run sync:opencode after changes to marketplace metadata, skills, agents, commands, or the plugin template, while retaining bun run check:opencode for verification. Update README.md lines 38-43 to document the marketplace metadata, source directories, and files that feed OpenCode generation.Source: Coding guidelines
.opencode/skills/research/scripts/evaluator.ts-612-623 (1)
612-623: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winA candidate-only scenario is reported as a quality regression.
Line 622 sets
qualityRegressionwhenbaselineRunis absent. A scenario that exists only in the candidate summary has no baseline counterpart. That happens whenever a new scenario is added toscenarios.jsonand the baseline artifacts predate it.compareSummariesthen returnsrecommendation: "reject"at Lines 667-668, which is the strongest verdict.The reverse case is already handled separately at Lines 625-635, where a baseline-only scenario means lost coverage. Distinguish new coverage from lost coverage.
♻️ Proposed fix
- if (!baselineRun || severity[candidateRun.status] > severity[baselineRun.status]) qualityRegression = true; + if (baselineRun && severity[candidateRun.status] > severity[baselineRun.status]) qualityRegression = true; + if (!baselineRun && candidateRun.status !== "pass") qualityRegression = true;If treating every new scenario as a rejection is intentional, add a comment that records the decision.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/skills/research/scripts/evaluator.ts around lines 612 - 623, Update the qualityRegression assignment in compareSummaries so candidate-only runs (when baselineRun is absent) do not count as regressions; only compare severity when both baselineRun and candidateRun exist. Preserve the separate baseline-only handling for lost coverage, and add a clarifying comment only if candidate-only scenarios are intentionally meant to reject.scripts/install-opencode.sh-20-25 (1)
20-25: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winPreserve an existing backup.
If
${link_path}.bakalready exists as a regular file,mvoverwrites it. This can destroy the only backup of user-managed OpenCode configuration.Fail before moving, or generate a unique backup name when
${link_path}.bakalready exists.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/install-opencode.sh` around lines 20 - 25, Update the backup handling before the ln command so an existing regular ${link_path}.bak is never overwritten: either fail before mv or select a unique unused backup path when the default exists, then move ${link_path} to that path and preserve the symlink creation flow.docs/plans/2026-08-02-opencode-support.md-7-7 (1)
7-7: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse the actual plugin filename.
The architecture summary names
plugin.ts, but the implementation createsbstack.tsinscripts/opencode-plugin/and.opencode/plugins/. Usebstack.tsconsistently to prevent incorrect file references.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/plans/2026-08-02-opencode-support.md` at line 7, Update the architecture summary to replace every reference to the generated plugin filename “plugin.ts” with “bstack.ts,” matching the implementation under scripts/opencode-plugin/ and .opencode/plugins/.docs/plans/2026-08-02-opencode-support.md-625-625 (1)
625-625: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDocument the actual collision behavior.
The generator exits with
Skill name collision; it does not resolve collisions through OpenCode configuration permissions. Tell users to rename the source skill or change the generator's collision policy.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/plans/2026-08-02-opencode-support.md` at line 625, Update the skill-name collision guidance in the OpenCode support plan to state that the generator exits with “Skill name collision” rather than resolving collisions through OpenCode configuration permissions. Instruct users to rename the source skill or change the generator’s collision policy.docs/plans/2026-08-02-opencode-support.md-608-629 (1)
608-629: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winFix the nested Markdown fences.
The
```bashfence at Line 617 closes the outer```markdownfence. The README example then renders as normal plan text, and the fence at Line 629 is parsed incorrectly. Use four backticks for the outer fence or indent the inner code block.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/plans/2026-08-02-opencode-support.md` around lines 608 - 629, The OpenCode compatibility section’s outer Markdown fence conflicts with its nested bash code fence. Update the outer fence around the section beginning with “## OpenCode 호환성” to use four backticks, preserving the inner ```bash block and all surrounding content.Source: Linters/SAST tools
.opencode/command/autoresearch.md-15-18 (1)
15-18: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winAdd blank lines around the fenced block.
markdownlint-cli2reports MD031 at Lines 16 and 18. Add one blank line before and after thebashfence.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/command/autoresearch.md around lines 15 - 18, Add a blank line immediately before and after the fenced bash block in the autoresearch instructions, preserving the existing command and surrounding text.Source: Linters/SAST tools
.opencode/skills/competitive-agents/SKILL.md-87-97 (1)
87-97: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winAdd blank lines after the headings.
markdownlint-cli2reports MD022 at Lines 89, 93, and 96. Add a blank line after each heading.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/skills/competitive-agents/SKILL.md around lines 87 - 97, Update the “Analyze and present in this format” template in SKILL.md by adding a blank line after each heading: “### Analysis,” “### Synthesized Solution,” and “### Rationale.” Preserve the existing content and formatting otherwise.Source: Linters/SAST tools
.opencode/skills/autoresearch/SKILL.md-144-150 (1)
144-150: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winUse a Conventional Commit subject for experiment keeps.
The keep path uses raw
<description>as the commit subject. It does not requiretype(scope): description, so semantic-release may not classify the commit correctly.Based on learnings, use Conventional Commits format:
type(scope): description.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/skills/autoresearch/SKILL.md around lines 144 - 150, Update the “If keep” commit command in the autoresearch instructions to require a Conventional Commit subject in the form type(scope): description instead of raw <description>. Preserve the existing result JSON in the commit body and apply this format specifically to experiment keep commits.Source: Learnings
.opencode/skills/autoresearch/SKILL.md-142-168 (1)
142-168: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winAdd blank lines around the shell fences.
markdownlint-cli2reports MD031 at Lines 145, 153, and 158. Add blank lines before and after each fenced block.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/skills/autoresearch/SKILL.md around lines 142 - 168, Add blank lines immediately before and after each shell fenced code block in the “Git operations” and “Append result to JSONL” sections of SKILL.md, including the fences around the keep, hash, discard/crash, and JSONL commands, so the markdown satisfies MD031.Source: Linters/SAST tools
🧹 Nitpick comments (5)
docs/plans/2026-08-02-opencode-support-design.md (1)
39-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a language identifier to this fenced block.
Line 39 has an unlabeled fenced block. Use
textfor the directory tree to satisfy MD040.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/plans/2026-08-02-opencode-support-design.md` around lines 39 - 46, Label the fenced directory-tree block in the design document with the text language identifier by changing its opening fence to use text; leave the tree content unchanged.Source: Linters/SAST tools
.opencode/skills/research/scripts/evaluate.ts (1)
262-264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winScore the run once, after the harness delegation event is added.
aggregatealready callsscoreRunat Line 195. Line 264 callsscoreRuna second time. The second call is required only for theresearcherroute, because Line 263 appends the harness delegation event after the first scoring. For thedirectroute the second call repeats identical work, and the intermediaterunvalue carriesstatusandassertionsthat do not reflect the appended event.Pass the extra event into
aggregateso scoring happens once.♻️ Proposed refactor
-function aggregate(results: RuntimeResult[], answer: StructuredAnswer, route: RouteName, scenario: Scenario, instructions: Instructions, runtime: RuntimeName, variant: VariantName): EvaluationRun { +function aggregate(results: RuntimeResult[], answer: StructuredAnswer, route: RouteName, scenario: Scenario, instructions: Instructions, runtime: RuntimeName, variant: VariantName, extraEvents: NormalizedEvent[] = []): EvaluationRun { const stdoutLines = results.flatMap((result) => result.stdoutLines); let events; try { events = normalizeEvents(runtime, stdoutLines); } catch (error) { const detail = error instanceof Error ? error.message : String(error); results.push({ exitCode: 1, stdoutLines: [], stderr: detail, finalJson: undefined, elapsedMs: 0, availability: "available" }); events = []; } + events = [...events, ...extraEvents];- const run = aggregate([routeResult, execution], answer, route.route, scenario, instructions, runtime, variant); - if (route.route === "researcher") run.events.push({ action: "delegate", tool: "harness", rawType: "harness" }); - return scoreRun(run); + const harnessEvents: NormalizedEvent[] = route.route === "researcher" + ? [{ action: "delegate", tool: "harness", rawType: "harness" }] + : []; + return aggregate([routeResult, execution], answer, route.route, scenario, instructions, runtime, variant, harnessEvents);This requires importing
type NormalizedEventfrom./evaluator.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/skills/research/scripts/evaluate.ts around lines 262 - 264, Update aggregate and its caller so the optional harness delegation event is supplied to aggregate before scoring, using the NormalizedEvent type from ./evaluator. Remove the post-aggregate run.events mutation and the separate scoreRun call, ensuring both researcher and direct routes are scored exactly once with the complete event set..opencode/skills/research/scripts/runtime-adapters.ts (1)
164-177: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSkip unparseable stdout lines instead of failing the whole run.
Line 167 calls
JSON.parse(line)with no guard. One non-JSON line on stdout, for example a CLI warning, throws. The caller at Line 238 converts that throw intoexitCode: 1, so the run is discarded even though theresultevent may be present.runtimeErrorMessageat Lines 104-119 already skips unparseable lines. Use the same handling.♻️ Proposed fix
for (const line of stdoutLines) { - const value = JSON.parse(line) as unknown; + let value: unknown; + try { + value = JSON.parse(line) as unknown; + } catch { + continue; + } if (typeof value === "object" && value !== null && !Array.isArray(value)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/skills/research/scripts/runtime-adapters.ts around lines 164 - 177, Update parseClaudeResult to catch JSON.parse failures for individual stdout lines and continue scanning subsequent lines, matching the tolerant handling already used by runtimeErrorMessage. Preserve result-event detection and downstream structured_output/result parsing unchanged, while still throwing when no valid result event is found..opencode/skills/research/scripts/evaluator.ts (1)
326-329: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winMake
.keyliteral before narrowingJsonObject.
valueisJsonObject(Record<string, unknown>), andfor (const key of ["url", "ref_id", "href"])inferskey: stringbecause the array is notas const.value[key]remainsunknown, so the string guard and return are not visible to TypeScript control-flow analysis. Use["url", "ref_id", "href"] as constor bindvalue[key]to a local variable before thetypeof candidate === "string"check.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.opencode/skills/research/scripts/evaluator.ts around lines 326 - 329, Update the key iteration in the value extraction logic to preserve literal key types by making the ["url", "ref_id", "href"] array const, or bind value[key] to a local candidate before narrowing it. Ensure the typeof check and returned value are recognized as string by TypeScript while preserving the existing URL validation behavior.docs/plans/2026-08-02-opencode-support.md (1)
388-400: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winCompare skill contents, not only file names.
This test claims to verify a verbatim copy but compares only relative paths. Generated files can change content and the test will still pass. Add a recursive content comparison such as
diff -ru.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/plans/2026-08-02-opencode-support.md` around lines 388 - 400, Update the “opencode bundle copies skill subdirectories verbatim” test to compare the source and bundled skill directory contents recursively, using a content-aware comparison such as diff -ru, while retaining coverage for the complete directory tree.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/sync-marketplace.yml:
- Around line 32-46: Update the sync workflow’s change-detection and staging
commands to include the `plugins/*/.claude-plugin/plugin.json` pathspec
alongside the existing manifest paths. Add it to both the `git diff --quiet`
check and the corresponding `git add` command, preserving the requirement that
all plugin manifests are synchronized and committed.
In @.opencode/command/autoresearch.md:
- Around line 13-25: In .opencode/command/autoresearch.md lines 13-25, terminate
the “arguments = off” branch immediately after creating the sentinel and
reporting the paused state so resume or fresh-start logic cannot delete it. In
.opencode/skills/autoresearch/SKILL.md lines 223-234, check for
.autoresearch/off before every iteration and stop when present, while enforcing
configured run-count, time, and cost limits.
In @.opencode/skills/autoresearch/SKILL.md:
- Around line 112-118: The benchmark wrapper around .autoresearch/run.sh must
preserve the benchmark’s exit status and avoid shared output files. Replace the
fixed /tmp/autoresearch-output.txt path with a unique per-run temporary file,
and capture the script’s status using PIPESTATUS[0] (or enable pipefail) before
subsequent commands overwrite pipeline status.
- Around line 142-161: Update the Git operations section in SKILL.md to restrict
staging, reverting, and cleaning to explicitly recorded experiment-owned paths
instead of using repository-wide commands. Preserve the keep flow’s commit and
hash retrieval, and ensure discard or crash cleanup cannot modify unrelated user
files; alternatively require a dedicated worktree for the experiment.
- Around line 165-169: Update the result-appending command in the “Append result
to JSONL” section to construct each record with a JSON serializer such as jq
-cn, passing run, commit, metric, metrics, status, description, timestamp, and
segment as typed values. Remove the hand-built echo template so quotes,
backslashes, and newlines are escaped correctly while preserving the existing
JSONL fields.
In @.opencode/skills/create-pr/scripts/wait-for-merge.sh:
- Around line 39-43: Update the polling logic around the state variable and gh
pr merge command to handle CLOSED as a distinct terminal outcome, preventing
merge attempts and AWAITING_REVIEW reporting for closed PRs. Preserve MERGED
handling, but stop suppressing merge failure details and return a distinct
non-review error status with the underlying failure information for merge,
conflict, or API errors.
- Around line 21-37: Update the polling loop in wait-for-merge.sh to capture the
exit status of gh pr checks instead of converting command failures to an empty
JSON array, and handle authentication, network, or CLI errors explicitly. Treat
a valid empty check response as complete rather than polling forever, while
preserving failure/cancellation detection for returned checks. Add a bounded
wait or cancellation path so the loop cannot run indefinitely.
In @.opencode/skills/research/scripts/evaluate.ts:
- Around line 301-309: In .opencode/skills/research/scripts/evaluate.ts, add a
validateRun helper that verifies runtime, variant, status, scenario.id, and
instructionHashes.skill/researcher have the expected types, calling usage("run
artifact is malformed") for invalid data. Apply it to parsed files before
pushing into existingRuns at lines 301-309 and to each sourceRuns entry before
the identity checks at lines 405-412, so both paths operate only on validated
EvaluationRun objects.
- Around line 458-462: Update compare to group or match baseline and candidate
summaries by their summary.runtime value before invoking compareSummaries, so
each comparison only pairs summaries from the same runtime. Preserve the
existing comparison aggregation and rejection behavior for genuine cross-runtime
absence or regressions.
- Around line 21-25: Update the root and resource path setup in the evaluate
script so it resolves the repository root reliably from a repository marker
rather than climbing beyond the checkout. Point skill, researcher, scenario, and
schema paths at the generated .opencode layout, ensuring gitCommit() and bun
check:opencode operate within the repository.
In @.opencode/skills/research/scripts/evaluator.ts:
- Around line 371-377: Update normalizeEvents so token events are emitted only
from the intended top-level token record, not every nested mapping visited
through visit. Prevent traversal of Codex token_count fields such as
total_token_usage and last_token_usage from producing additional events, while
preserving one deterministic event with the relevant input and output token
counts for aggregate.
In @.opencode/skills/research/scripts/runtime-adapters.ts:
- Around line 129-144: Update the Bun.spawn call in the runtime adapter to
configure a finite timeout and killSignal so hung CLI processes are terminated
and child.exited cannot block indefinitely. Also await child.stdin.end() in the
input path and handle a rejection from early child termination to prevent an
unhandled promise rejection.
In @.opencode/skills/verify/SKILL.md:
- Around line 4-11: Align the allowed-tools list in the verify skill with its
non-mutating contract by removing the write-capable Write and Edit entries.
Preserve the remaining read, search, execution, and delegation tools, and leave
the contract unchanged.
In `@docs/plans/2026-08-02-opencode-support.md`:
- Line 34: Update the Git command blocking logic represented by the BLOCKS
patterns and trailer check to detect hook bypasses using `-c
core.hooksPath=/dev/null`, including when Git options appear before `commit`.
Preserve blocking for the existing `git config ... core.hooksPath` form and
ensure both argument orders are covered.
- Around line 153-154: Update the bundle replacement sequence around tmp_root
and its cleanup trap so the existing .opencode bundle remains intact until the
temporary tree has been installed successfully. Avoid removing the current
bundle before the move; use an atomic or rollback-safe replacement flow, and
ensure cleanup does not delete the preserved bundle if installation fails.
In `@scripts/generate-opencode-artifacts.sh`:
- Around line 93-97: The bundle replacement logic around generated_any must not
leave an existing OPENCODE_ROOT when no eligible plugin directories are
generated. Unconditionally replace the bundle after successful generation, or
explicitly fail when generated_any is zero; ensure the script never exits
successfully while preserving stale artifacts.
In `@scripts/opencode-plugin/bstack.ts`:
- Around line 18-38: The Git hook guard must block all supported bypass forms,
including short -n, git -c core.hooksPath=... commit invocations, and Git
commands within compound shell commands. Update isGitCommand and the related
checks in scripts/opencode-plugin/bstack.ts to conservatively inspect compound
commands and detect these forms, then regenerate .opencode/plugins/bstack.ts
from the corrected source with the same changes.
---
Outside diff comments:
In @.opencode/skills/ship/SKILL.md:
- Around line 1-104: Regenerate the stale ship skill artifact by updating the
source content in plugins/me/skills/ship/SKILL.md rather than editing
.opencode/skills/ship/SKILL.md directly. Run bun run sync:opencode, verify the
generated bundle with bun run check:opencode, and commit the resulting generated
changes.
---
Minor comments:
In @.opencode/command/autoresearch.md:
- Around line 15-18: Add a blank line immediately before and after the fenced
bash block in the autoresearch instructions, preserving the existing command and
surrounding text.
In @.opencode/skills/autoresearch/SKILL.md:
- Around line 144-150: Update the “If keep” commit command in the autoresearch
instructions to require a Conventional Commit subject in the form type(scope):
description instead of raw <description>. Preserve the existing result JSON in
the commit body and apply this format specifically to experiment keep commits.
- Around line 142-168: Add blank lines immediately before and after each shell
fenced code block in the “Git operations” and “Append result to JSONL” sections
of SKILL.md, including the fences around the keep, hash, discard/crash, and
JSONL commands, so the markdown satisfies MD031.
In @.opencode/skills/competitive-agents/SKILL.md:
- Around line 87-97: Update the “Analyze and present in this format” template in
SKILL.md by adding a blank line after each heading: “### Analysis,” “###
Synthesized Solution,” and “### Rationale.” Preserve the existing content and
formatting otherwise.
In @.opencode/skills/research/scripts/evaluator.ts:
- Around line 612-623: Update the qualityRegression assignment in
compareSummaries so candidate-only runs (when baselineRun is absent) do not
count as regressions; only compare severity when both baselineRun and
candidateRun exist. Preserve the separate baseline-only handling for lost
coverage, and add a clarifying comment only if candidate-only scenarios are
intentionally meant to reject.
In `@CLAUDE.md`:
- Around line 58-59: Update CLAUDE.md lines 58-59 to require bun run
sync:opencode after changes to marketplace metadata, skills, agents, commands,
or the plugin template, while retaining bun run check:opencode for verification.
Update README.md lines 38-43 to document the marketplace metadata, source
directories, and files that feed OpenCode generation.
In `@docs/plans/2026-08-02-opencode-support.md`:
- Line 7: Update the architecture summary to replace every reference to the
generated plugin filename “plugin.ts” with “bstack.ts,” matching the
implementation under scripts/opencode-plugin/ and .opencode/plugins/.
- Line 625: Update the skill-name collision guidance in the OpenCode support
plan to state that the generator exits with “Skill name collision” rather than
resolving collisions through OpenCode configuration permissions. Instruct users
to rename the source skill or change the generator’s collision policy.
- Around line 608-629: The OpenCode compatibility section’s outer Markdown fence
conflicts with its nested bash code fence. Update the outer fence around the
section beginning with “## OpenCode 호환성” to use four backticks, preserving the
inner ```bash block and all surrounding content.
In `@scripts/install-opencode.sh`:
- Around line 20-25: Update the backup handling before the ln command so an
existing regular ${link_path}.bak is never overwritten: either fail before mv or
select a unique unused backup path when the default exists, then move
${link_path} to that path and preserve the symlink creation flow.
---
Nitpick comments:
In @.opencode/skills/research/scripts/evaluate.ts:
- Around line 262-264: Update aggregate and its caller so the optional harness
delegation event is supplied to aggregate before scoring, using the
NormalizedEvent type from ./evaluator. Remove the post-aggregate run.events
mutation and the separate scoreRun call, ensuring both researcher and direct
routes are scored exactly once with the complete event set.
In @.opencode/skills/research/scripts/evaluator.ts:
- Around line 326-329: Update the key iteration in the value extraction logic to
preserve literal key types by making the ["url", "ref_id", "href"] array const,
or bind value[key] to a local candidate before narrowing it. Ensure the typeof
check and returned value are recognized as string by TypeScript while preserving
the existing URL validation behavior.
In @.opencode/skills/research/scripts/runtime-adapters.ts:
- Around line 164-177: Update parseClaudeResult to catch JSON.parse failures for
individual stdout lines and continue scanning subsequent lines, matching the
tolerant handling already used by runtimeErrorMessage. Preserve result-event
detection and downstream structured_output/result parsing unchanged, while still
throwing when no valid result event is found.
In `@docs/plans/2026-08-02-opencode-support-design.md`:
- Around line 39-46: Label the fenced directory-tree block in the design
document with the text language identifier by changing its opening fence to use
text; leave the tree content unchanged.
In `@docs/plans/2026-08-02-opencode-support.md`:
- Around line 388-400: Update the “opencode bundle copies skill subdirectories
verbatim” test to compare the source and bundled skill directory contents
recursively, using a content-aware comparison such as diff -ru, while retaining
coverage for the complete directory tree.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 8fb57dbe-eaba-45b3-b0cd-8b1bc63c00ee
📒 Files selected for processing (46)
.github/workflows/ci.yml.github/workflows/release.yml.github/workflows/sync-marketplace.yml.opencode/agents/code-reviewer.md.opencode/agents/researcher.md.opencode/agents/security-auditor.md.opencode/agents/test-engineer.md.opencode/command/autoresearch.md.opencode/plugins/bstack.ts.opencode/skills/autoresearch/SKILL.md.opencode/skills/competitive-agents/SKILL.md.opencode/skills/create-pr/SKILL.md.opencode/skills/create-pr/scripts/preflight-check.sh.opencode/skills/create-pr/scripts/wait-for-merge.sh.opencode/skills/datadog/SKILL.md.opencode/skills/e2e-scenario-testing/SKILL.md.opencode/skills/handoff/SKILL.md.opencode/skills/research/SKILL.md.opencode/skills/research/evals/result.schema.json.opencode/skills/research/evals/scenarios.json.opencode/skills/research/scripts/evaluate.ts.opencode/skills/research/scripts/evaluator.ts.opencode/skills/research/scripts/runtime-adapters.ts.opencode/skills/ship/SKILL.md.opencode/skills/story-loop/SKILL.md.opencode/skills/verify/SKILL.md.opencode/skills/verify/references/exploration-guide.md.opencode/skills/verify/references/issue-taxonomy.md.opencode/skills/verify/templates/report-template.md.opencode/skills/write-skill/SKILL.md.opencode/skills/write-skill/agents/openai.yaml.opencode/skills/writing-prds/SKILL.md.opencode/skills/writing-rfcs/SKILL.md.releaserc.jsCLAUDE.mdREADME.mddocs/plans/2026-08-02-opencode-support-design.mddocs/plans/2026-08-02-opencode-support.mdpackage.jsonscripts/check-opencode-artifacts.shscripts/generate-opencode-artifacts.shscripts/install-opencode.shscripts/opencode-plugin/bstack.tsscripts/sync-opencode-artifacts.shtests/github_workflows.batstests/opencode_artifacts.bats
| - name: Check for changes | ||
| id: check_changes | ||
| run: | | ||
| untracked_artifacts="$( | ||
| git ls-files --others --exclude-standard -- \ | ||
| .agents/plugins/marketplace.json \ | ||
| 'plugins/*/.codex-plugin/plugin.json' | ||
| 'plugins/*/.codex-plugin/plugin.json' \ | ||
| .opencode | ||
| )" | ||
|
|
||
| if git diff --quiet -- \ | ||
| .claude-plugin/marketplace.json \ | ||
| .agents/plugins/marketplace.json \ | ||
| 'plugins/*/.codex-plugin/plugin.json' && \ | ||
| 'plugins/*/.codex-plugin/plugin.json' \ | ||
| .opencode && \ |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Stage the source plugin manifests.
The workflow does not check or stage plugins/*/.claude-plugin/plugin.json. Version synchronization can leave those source manifests unstaged while committing the marketplace version. Add this path to both the git diff --quiet and git add pathspecs.
As per coding guidelines, “Version synchronization must update marketplace.json and every plugins/*/.claude-plugin/plugin.json; all plugins share one version.”
Proposed fix
if git diff --quiet -- \
.claude-plugin/marketplace.json \
.agents/plugins/marketplace.json \
+ 'plugins/*/.claude-plugin/plugin.json' \
'plugins/*/.codex-plugin/plugin.json' \
.opencode && \
@@
git add \
.claude-plugin/marketplace.json \
.agents/plugins/marketplace.json \
+ 'plugins/*/.claude-plugin/plugin.json' \
'plugins/*/.codex-plugin/plugin.json' \
.opencodeAlso applies to: 58-62
🧰 Tools
🪛 actionlint (1.7.12)
[error] 34-34: shellcheck reported issue in this script: SC2086:info:14:27: Double quote to prevent globbing and word splitting
(shellcheck)
[error] 34-34: shellcheck reported issue in this script: SC2086:info:16:26: Double quote to prevent globbing and word splitting
(shellcheck)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/sync-marketplace.yml around lines 32 - 46, Update the sync
workflow’s change-detection and staging commands to include the
`plugins/*/.claude-plugin/plugin.json` pathspec alongside the existing manifest
paths. Add it to both the `git diff --quiet` check and the corresponding `git
add` command, preserving the requirement that all plugin manifests are
synchronized and committed.
Source: Coding guidelines
| ### If arguments = "off" | ||
|
|
||
| Create a `.autoresearch/off` sentinel file in the current directory: | ||
| ```bash | ||
| mkdir -p .autoresearch && touch .autoresearch/off | ||
| ``` | ||
| Then tell the user autoresearch mode is paused. It can be resumed by running `/autoresearch` again (which will delete the sentinel). | ||
|
|
||
| ### If `.autoresearch/autoresearch.md` exists in the current directory (resume) | ||
|
|
||
| This is a resume. Do the following: | ||
|
|
||
| 1. Delete `.autoresearch/off` if it exists |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Make the autoresearch pause protocol reliable.
The command creates .autoresearch/off but can continue into logic that deletes it. The running skill also does not check the sentinel and has no resource limit.
.opencode/command/autoresearch.md#L13-L25: terminate theoffbranch before the resume or fresh-start branch..opencode/skills/autoresearch/SKILL.md#L223-L234: check.autoresearch/offbefore each iteration and enforce run, time, and cost limits.
🧰 Tools
🪛 markdownlint-cli2 (0.23.1)
[warning] 16-16: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
[warning] 18-18: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
📍 Affects 2 files
.opencode/command/autoresearch.md#L13-L25(this comment).opencode/skills/autoresearch/SKILL.md#L223-L234
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.opencode/command/autoresearch.md around lines 13 - 25, In
.opencode/command/autoresearch.md lines 13-25, terminate the “arguments = off”
branch immediately after creating the sentinel and reporting the paused state so
resume or fresh-start logic cannot delete it. In
.opencode/skills/autoresearch/SKILL.md lines 223-234, check for
.autoresearch/off before every iteration and stop when present, while enforcing
configured run-count, time, and cost limits.
Source: Linters/SAST tools
| ```bash | ||
| START_TIME=$(date +%s%N) | ||
| bash -c "./.autoresearch/run.sh" 2>&1 | tee /tmp/autoresearch-output.txt | ||
| EXIT_CODE=$? | ||
| END_TIME=$(date +%s%N) | ||
| DURATION=$(echo "scale=3; ($END_TIME - $START_TIME) / 1000000000" | bc) | ||
| echo "Duration: ${DURATION}s, Exit code: ${EXIT_CODE}" |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Preserve the benchmark status and isolate its output.
The pipeline returns tee's status unless the caller enables pipefail. A failing .autoresearch/run.sh can therefore be recorded as successful. The fixed /tmp/autoresearch-output.txt path can also mix output from concurrent loops. Use a per-run temporary file and capture the benchmark process status with ${PIPESTATUS[0]} or pipefail.
🧰 Tools
🪛 SkillSpector (2.4.4)
[warning] 225: [EA4] Unbounded Resource Access: Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.
Remediation: Set explicit rate limits, timeouts, and resource quotas for API calls, file operations, and compute. Implement circuit breakers for runaway loops.
(Excessive Agency (EA4))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.opencode/skills/autoresearch/SKILL.md around lines 112 - 118, The benchmark
wrapper around .autoresearch/run.sh must preserve the benchmark’s exit status
and avoid shared output files. Replace the fixed /tmp/autoresearch-output.txt
path with a unique per-run temporary file, and capture the script’s status using
PIPESTATUS[0] (or enable pipefail) before subsequent commands overwrite pipeline
status.
| ### 2. Git operations | ||
|
|
||
| **If keep:** | ||
| ```bash | ||
| git add -A | ||
| git diff --cached --quiet && echo "nothing to commit" || git commit -m "<description> | ||
|
|
||
| Result: {\"status\":\"keep\",\"<metricName>\":<value>,<secondary metrics>}" | ||
| ``` | ||
|
|
||
| Then get the new commit hash: | ||
| ```bash | ||
| git rev-parse --short=7 HEAD | ||
| ``` | ||
|
|
||
| **If discard or crash:** | ||
| ```bash | ||
| git checkout -- . | ||
| git clean -fd | ||
| ``` |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🔴 Critical | 🏗️ Heavy lift
Restrict Git operations to experiment-owned paths.
git add -A stages unrelated user changes. git checkout -- . and git clean -fd can revert or delete unrelated files. Use a dedicated worktree, or record the experiment-owned paths and restore only those paths. The current instructions can cause irreversible data loss and unrelated commits.
🧰 Tools
🪛 markdownlint-cli2 (0.23.1)
[warning] 145-145: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
[warning] 153-153: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
[warning] 158-158: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🪛 SkillSpector (2.4.4)
[warning] 225: [EA4] Unbounded Resource Access: Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.
Remediation: Set explicit rate limits, timeouts, and resource quotas for API calls, file operations, and compute. Implement circuit breakers for runaway loops.
(Excessive Agency (EA4))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.opencode/skills/autoresearch/SKILL.md around lines 142 - 161, Update the
Git operations section in SKILL.md to restrict staging, reverting, and cleaning
to explicitly recorded experiment-owned paths instead of using repository-wide
commands. Preserve the keep flow’s commit and hash retrieval, and ensure discard
or crash cleanup cannot modify unrelated user files; alternatively require a
dedicated worktree for the experiment.
| ### 3. Append result to JSONL | ||
|
|
||
| ```bash | ||
| echo '{"run":<N>,"commit":"<hash>","metric":<value>,"metrics":{<secondaries>},"status":"<status>","description":"<desc>","timestamp":'$(date +%s)',"segment":<seg>}' >> .autoresearch/autoresearch.jsonl | ||
| ``` |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Serialize result records with a JSON serializer.
The echo template inserts descriptions, metric names, and values without JSON escaping. A quote, backslash, or newline can corrupt .autoresearch/autoresearch.jsonl and break resume parsing. Use jq -cn or another JSON serializer.
🧰 Tools
🪛 SkillSpector (2.4.4)
[warning] 225: [EA4] Unbounded Resource Access: Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.
Remediation: Set explicit rate limits, timeouts, and resource quotas for API calls, file operations, and compute. Implement circuit breakers for runaway loops.
(Excessive Agency (EA4))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.opencode/skills/autoresearch/SKILL.md around lines 165 - 169, Update the
result-appending command in the “Append result to JSONL” section to construct
each record with a JSON serializer such as jq -cn, passing run, commit, metric,
metrics, status, description, timestamp, and segment as typed values. Remove the
hand-built echo template so quotes, backslashes, and newlines are escaped
correctly while preserving the existing JSONL fields.
| allowed-tools: | ||
| - Bash | ||
| - Read | ||
| - Write | ||
| - Edit | ||
| - Glob | ||
| - Grep | ||
| - Agent |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Tracked files under .opencode:\n'
git ls-files '.opencode/**' | sed -n '1,200p'
printf '\nTarget file with line numbers:\n'
if [ -f .opencode/skills/verify/SKILL.md ]; then
nl -ba .opencode/skills/verify/SKILL.md | sed -n '1,220p'
else
echo 'target file missing'
fi
printf '\nSearch for /verify, verify skill references, and allowed-tools:\n'
rg -n "(/verify|verify|allowed-tools|Write|Edit)" .opencode || true
printf '\nPackage/check scripts:\n'
if [ -f package.json ]; then
jq -r '.scripts // {} | to_entries[] | "\(.key): \(.value)"' package.json | rg -n 'opencode|check' || true
fiRepository: baleen37/bstack
Length of output: 1503
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Package/check scripts:\n'
if [ -f package.json ]; then
node -e "const p=require('./package.json'); const s=p.scripts||{}; for (const k of Object.keys(s).sort()) if (/opencode|check/i.test(k)) console.log(k+': '+s[k]);" || true
fi
printf '\nRelevant verify skill lines (grep and read-only parse):\n'
python3 - <<'PY'
from pathlib import Path
p=Path('.opencode/skills/verify/SKILL.md')
print(f'exists={p.exists}')
lines=p.read_text().splitlines()
for i,l in enumerate(lines,1):
if i<=220:
print(f'{i:4d}: {l}')
PY
printf '\nSearch verify contract/permissions across .opencode:\n'
python3 - <<'PY'
from pathlib import Path
terms=['/verify','does not fix','allowed-tools','Write','Edit','Write']
for p in sorted(Path('.opencode').rglob('*')):
if p.is_file() and p.suffix.lower()=='.md':
text=p.read_text(errors='ignore').splitlines()
for i,l in enumerate(text,1):
if any(t in l for t in terms) or p == Path('.opencode/skills/verify/SKILL.md'):
if i<=250 or p != Path('.opencode/skills/verify/SKILL.md'):
print(f'{p}:{i}: {l}')
PYRepository: baleen37/bstack
Length of output: 18477
🌐 Web query:
OpenCode skills allowed-tools Write Edit tool permission documentation
💡 Result:
In OpenCode, the "write" and "edit" tools are managed under the unified "edit" permission [1][2][3]. The "edit" permission covers all file modification actions, including "edit", "write", and "apply_patch" [1][4][5]. Key details regarding OpenCode permissions: Configuration: Permissions are defined in the "permission" field of your configuration [1][5]. As of version 1.1.1, the legacy "tools" boolean configuration has been deprecated in favor of this granular permission system, though it remains supported for backwards compatibility [1][3][5]. Permission Actions: Each permission can be set to one of three states [5]: - "allow": Executes the action without approval [5][6]. - "ask": Prompts the user for approval [5][6]. - "deny": Blocks the action [5][6]. Granular Control: You can set permissions globally (using a "" wildcard) or for specific tools [1][5]. For tools like "edit" and "bash", you can provide an object syntax to match specific file patterns or commands [1][3][6]. For example, you can allow editing for specific file types while denying it globally [3][6]: { "permission": { "edit": { ".mdx": "allow", "*": "deny" } } } When OpenCode prompts for approval (the "ask" state), you can choose to approve the action "once," "always" (for the duration of the session), or "reject" it [5].
Citations:
- 1: https://opencode.ai/docs/permissions/
- 2: https://open-code.ai/en/docs/tools
- 3: https://github.com/sst/opencode/blob/9ad6588f/packages/web/src/content/docs/permissions.mdx
- 4: https://opencode.ai/docs/agents/
- 5: https://open-code.ai/en/docs/permissions
- 6: Permission rework anomalyco/opencode#6319
Align allowed-tools with the non-mutating /verify contract.
/verify states it does not fix code, but allowed-tools grants Write and Edit. These tools enable file changes in the repository. Remove write-capable tools, or update the contract to allow mutation and name it clearly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.opencode/skills/verify/SKILL.md around lines 4 - 11, Align the
allowed-tools list in the verify skill with its non-mutating contract by
removing the write-capable Write and Edit entries. Preserve the remaining read,
search, execution, and delegation tools, and leave the contract unchanged.
| **Step 1: Write the plugin template** | ||
|
|
||
| ```ts | ||
| const PLUGIN_ROOT = new URL("..", import.meta.url).pathname.replace(/\/$/, "") |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Block hook bypasses that use Git options.
git -c core.hooksPath=/dev/null commit passes isGitCommand, but no BLOCKS pattern matches it. The existing rule only matches git config ... core.hooksPath, and the trailer check requires commit immediately after git. Add argument-order coverage or parse the command before checking it.
Also applies to: 36-48, 69-71
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/plans/2026-08-02-opencode-support.md` at line 34, Update the Git command
blocking logic represented by the BLOCKS patterns and trailer check to detect
hook bypasses using `-c core.hooksPath=/dev/null`, including when Git options
appear before `commit`. Preserve blocking for the existing `git config ...
core.hooksPath` form and ensure both argument orders are covered.
| tmp_root="$(mktemp -d "${OPENCODE_ROOT}.tmp.XXXXXX")" | ||
| trap 'rm -rf "${tmp_root}"' EXIT |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Preserve the current bundle until replacement succeeds.
The script removes .opencode/ before moving the temporary tree. If mv fails, the old bundle is already gone and the cleanup trap removes the temporary tree. Use a replacement sequence that preserves the current bundle until the new tree is installed successfully.
Also applies to: 197-200
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/plans/2026-08-02-opencode-support.md` around lines 153 - 154, Update the
bundle replacement sequence around tmp_root and its cleanup trap so the existing
.opencode bundle remains intact until the temporary tree has been installed
successfully. Avoid removing the current bundle before the move; use an atomic
or rollback-safe replacement flow, and ensure cleanup does not delete the
preserved bundle if installation fails.
| if [ "${generated_any}" -eq 1 ]; then | ||
| rm -rf "${OPENCODE_ROOT}" | ||
| mv "${tmp_root}" "${OPENCODE_ROOT}" | ||
| trap - EXIT | ||
| fi |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Do not preserve stale artifacts when no plugin is generated.
If eligible_plugins produces no existing plugin directory, generated_any stays 0. The script then exits successfully and leaves the previous .opencode/ bundle unchanged. This makes removed marketplace artifacts appear current.
Replace the bundle unconditionally after successful generation, or fail explicitly when zero source plugins are invalid. Do not return success with stale generated output.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/generate-opencode-artifacts.sh` around lines 93 - 97, The bundle
replacement logic around generated_any must not leave an existing OPENCODE_ROOT
when no eligible plugin directories are generated. Unconditionally replace the
bundle after successful generation, or explicitly fail when generated_any is
zero; ensure the script never exits successfully while preserving stale
artifacts.
| const isGitCommand = (command: string): boolean => /^\s*(\S*=\S*\s+)*git\s+/.test(command) | ||
|
|
||
| export const Bstack = async () => { | ||
| return { | ||
| "tool.execute.before": async ( | ||
| input: { tool: string }, | ||
| output: { args?: { command?: string } }, | ||
| ) => { | ||
| if (input.tool !== "bash") return | ||
| const command = output.args?.command ?? "" | ||
| if (!isGitCommand(command)) return | ||
|
|
||
| for (const block of BLOCKS) { | ||
| if (block.pattern.test(command)) { | ||
| throw new Error(block.info ? `${block.message}\n${block.info}` : block.message) | ||
| } | ||
| } | ||
|
|
||
| if (/git\s+commit/.test(command) && /Co-Authored-By:/.test(command)) { | ||
| throw new Error("Co-Authored-By trailers are not allowed in commit messages\nPlease remove 'Co-Authored-By:' from your commit message.") | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Block all supported Git hook-bypass forms.
The guard allows git commit -n, which is Git’s short form of --no-verify. It also ignores compound commands such as cd repo && git commit --no-verify because isGitCommand() requires git at the command start. A command such as git -c core.hooksPath=/dev/null commit also bypasses the current core.hooksPath check.
scripts/opencode-plugin/bstack.ts#L18-L38: parse or conservatively inspect compound shell commands, and block-nplusgit -c core.hooksPath=...bypasses..opencode/plugins/bstack.ts#L18-L38: regenerate this artifact from the corrected source plugin.
Based on learnings: "Never bypass pre-commit hooks with --no-verify."
📍 Affects 2 files
scripts/opencode-plugin/bstack.ts#L18-L38(this comment).opencode/plugins/bstack.ts#L18-L38
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/opencode-plugin/bstack.ts` around lines 18 - 38, The Git hook guard
must block all supported bypass forms, including short -n, git -c
core.hooksPath=... commit invocations, and Git commands within compound shell
commands. Update isGitCommand and the related checks in
scripts/opencode-plugin/bstack.ts to conservatively inspect compound commands
and detect these forms, then regenerate .opencode/plugins/bstack.ts from the
corrected source with the same changes.
Source: Learnings
Summary
.opencode/) from the Claude Code source of truth viabun run sync:opencode(generatorscripts/generate-opencode-artifacts.sh, templatescripts/opencode-plugin/bstack.ts)bun run check:opencode), symlink installer (bun run install:opencode→~/.config/opencode/), andbstack.tscommit-guard plugin (blocks--no-verify, hook bypasses).opencode/into semantic-release (prepare sync + commit assets) and CI/release/sync workflows (sync + drift steps)Test Plan
bun run test— all BATS suites passbun run check:opencode— no driftpre-commit run --all-files— only pre-existing markdownlint violations (present on main before this branch)install:opencodesymlinks 13 skills, 4 agents, command, pluginSummary by CodeRabbit
New Features
Documentation
Tests