Skip to content

Add OpenCode TypeScript plugin and skills install/remove support with tests - #1

Open
ZeR020 wants to merge 1 commit into
mainfrom
codex/fix-merge-conflicts-in-pr-616
Open

ZeR020 wants to merge 1 commit into
mainfrom
codex/fix-merge-conflicts-in-pr-616

Conversation

@ZeR020

@ZeR020 ZeR020 commented Jul 29, 2026 •

Copy link
Copy Markdown
Owner

Motivation

  • Provide automatic installation of an OpenCode TypeScript plugin that augments grep/glob tool output and injects a session reminder, and ship bundled Agent SKILL files for OpenCode installs.
  • Make the OpenCode integration idempotent, safe in plan/dry-run modes, and ensure embedded binary paths are JSON-escaped to avoid injection or syntax errors.

Description

  • Add an embedded TypeScript plugin template opencode_plugin_content that wires tool.execute.after (for grep/glob augmentation) and experimental.chat.system.transform (session reminder) and embeds the CLI binary path.
  • Implement json_escape_string and new functions cbm_upsert_opencode_plugin and cbm_remove_opencode_plugin to write/remove ~/.config/opencode/plugins/cbm-augment.ts, honoring dry_run and setting file permissions (fchmod/chmod) similarly to other install helpers.
  • Integrate plugin and skills handling into the install/uninstall flow: record planned writes in g_install_plan, create skills directory and call cbm_install_skills, and remove skills + plugin during uninstall; add CMM_OPENCODE_PLUGIN_NAME constant.
  • Expose new prototypes in cli.h for cbm_upsert_opencode_plugin and cbm_remove_opencode_plugin and update related install/uninstall logic to print summary messages.

Testing

  • Added unit tests in tests/test_cli.c covering cbm_upsert_opencode_plugin (fresh, idempotent, path escaping), cbm_remove_opencode_plugin, and cbm_install_skills/cbm_remove_skills behavior, and all new tests passed locally.
  • Existing CLI test suite was run with the new tests included and completed successfully.

Codex Task


Open in Devin Review

Summary by CodeRabbit

  • New Features
    • OpenCode setup now installs an integration plugin that adds relevant context to tool results and session reminders.
    • OpenCode skills are installed automatically alongside MCP configuration.
    • Added support for previewing these changes in plan or dry-run mode.
  • Bug Fixes
    • Improved handling of paths and quoting across platforms.
    • Plugin updates are safe to repeat and automatically replace outdated versions.
  • Improvements
    • OpenCode uninstall now removes the installed plugin and skills cleanly.

Copilot AI review requested due to automatic review settings July 29, 2026 14:53
@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

OpenCode installation now deploys skills, MCP configuration, and a TypeScript augmentation plugin. The plugin adds tool-result context and a session reminder. Uninstallation removes the skills and plugin. Tests cover creation, updates, escaping, removal, and skills content.

Changes

OpenCode lifecycle

Layer / File(s) Summary
Plugin contract and file management
src/cli/cli.h, src/cli/cli.c
Adds public plugin lifecycle APIs, embeds the TypeScript plugin, escapes the binary path, and manages the generated plugin file.
OpenCode installation and removal
src/cli/cli.c, tests/test_cli.c
Extends install and uninstall flows for OpenCode skills and the plugin, with tests for plugin updates, path escaping, removal, and skill content.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding OpenCode TypeScript plugin and skills install/remove support with tests.
Description check ✅ Passed The description is detailed and covers motivation, implementation, and testing; it mostly satisfies the template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-merge-conflicts-in-pr-616

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.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@src/cli/cli.c`:
- Around line 1836-1840: Update the plugin removal logic to call
cbm_unlink(plugin_path) directly instead of checking stat(), so dangling
symlinks are removed. Suppress only ENOENT as a successful absence; propagate or
return errors for all other unlink failures.
- Around line 551-560: The hook-augment subprocess flow must not wait
indefinitely or leak asynchronous stdin errors. Update the generated code around
spawn and the child close/error promise to attach a child.stdin error listener,
add a bounded timeout that kills the child if it hangs, and clear the timeout
when the child exits through either close or error.
- Around line 1812-1822: Update the plugin creation helper around the shown
file-write flow to check fchmod() and fclose(), returning CLI_ERR when either
fails; ensure cleanup is preserved. In src/cli/cli.c lines 3332-3336, check
cbm_upsert_opencode_plugin() and only print the installed-plugin success message
on success, otherwise propagate or report the failure. In src/cli/cli.c lines
3839-3842, apply the same handling to cbm_remove_opencode_plugin() before
printing the removal success message.
🪄 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: cea285a7-efec-41e8-a872-2e80f6539383

📥 Commits

Reviewing files that changed from the base of the PR and between 34efbc0 and 14f3168.

📒 Files selected for processing (3)
  • src/cli/cli.c
  • src/cli/cli.h
  • tests/test_cli.c

Comment thread src/cli/cli.c
Comment on lines +551 to +560
" const child = spawn(BIN, [\"hook-augment\"], "
"{ stdio: [\"pipe\", \"pipe\", \"ignore\"] })\n"
" child.stdin.write(payload)\n"
" child.stdin.end()\n"
" let stdout = \"\"\n"
" child.stdout.on(\"data\", (d: Buffer) => { stdout += d.toString() })\n"
" await new Promise<void>((resolve) => {\n"
" child.on(\"close\", () => resolve())\n"
" child.on(\"error\", () => resolve())\n"
" })\n"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound and safely handle the augmentation subprocess.

A hung hook-augment child makes the OpenCode grep/glob hook await forever. Also, child.stdin can emit an asynchronous error that the surrounding try/catch will not catch. Add a timeout that kills the child and attach an stdin error listener.

🤖 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 `@src/cli/cli.c` around lines 551 - 560, The hook-augment subprocess flow must
not wait indefinitely or leak asynchronous stdin errors. Update the generated
code around spawn and the child close/error promise to attach a child.stdin
error listener, add a bounded timeout that kills the child if it hangs, and
clear the timeout when the child exits through either close or error.

Comment thread src/cli/cli.c
Comment on lines +1812 to +1822
if (fprintf(f, opencode_plugin_content, escaped_binary_path) < 0) {
(void)fclose(f);
return CLI_ERR;
}
#ifndef _WIN32
fchmod(fileno(f), CLI_OCTAL_PERM);
#endif
(void)fclose(f);
#ifdef _WIN32
chmod(plugin_path, CLI_OCTAL_PERM);
#endif

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not report plugin lifecycle operations as successful when they fail.

fclose() can surface a buffered write failure, while fchmod() can fail; both are ignored. The install and uninstall dispatch then discard helper return codes and print success regardless.

  • src/cli/cli.c#L1812-L1822: check fchmod() and fclose() and return CLI_ERR on failure.
  • src/cli/cli.c#L3332-L3336: check cbm_upsert_opencode_plugin() before printing the installed-plugin message and propagate/report failure.
  • src/cli/cli.c#L3839-L3842: check cbm_remove_opencode_plugin() before printing the removal message and propagate/report failure.
📍 Affects 1 file
  • src/cli/cli.c#L1812-L1822 (this comment)
  • src/cli/cli.c#L3332-L3336
  • src/cli/cli.c#L3839-L3842
🤖 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 `@src/cli/cli.c` around lines 1812 - 1822, Update the plugin creation helper
around the shown file-write flow to check fchmod() and fclose(), returning
CLI_ERR when either fails; ensure cleanup is preserved. In src/cli/cli.c lines
3332-3336, check cbm_upsert_opencode_plugin() and only print the
installed-plugin success message on success, otherwise propagate or report the
failure. In src/cli/cli.c lines 3839-3842, apply the same handling to
cbm_remove_opencode_plugin() before printing the removal success message.

Comment thread src/cli/cli.c
Comment on lines +1836 to +1840
struct stat st;
if (stat(plugin_path, &st) == 0) {
return cbm_unlink(plugin_path);
}
return 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the plugin path even when it is a dangling symlink.

stat() follows links, so a dangling cbm-augment.ts returns ENOENT and is left behind while uninstall reports success. Call cbm_unlink() directly and suppress only ENOENT; return an error for other failures.

🤖 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 `@src/cli/cli.c` around lines 1836 - 1840, Update the plugin removal logic to
call cbm_unlink(plugin_path) directly instead of checking stat(), so dangling
symlinks are removed. Suppress only ENOENT as a successful absence; propagate or
return errors for all other unlink failures.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the CLI’s OpenCode integration by adding an auto-discovered TypeScript plugin (for grep/glob augmentation + session reminders) and wiring OpenCode skills install/remove into the install/uninstall flows, with accompanying unit tests.

Changes:

  • Add OpenCode TypeScript plugin template and install/remove helpers (cbm_upsert_opencode_plugin, cbm_remove_opencode_plugin) with JSON escaping for embedded binary paths.
  • Integrate OpenCode plugin + skills into install/uninstall and install-plan recording.
  • Add unit tests covering plugin upsert/remove and OpenCode skills install/remove.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
tests/test_cli.c Adds tests for OpenCode plugin creation/idempotence/path-escaping and skills install/remove.
src/cli/cli.h Exposes new OpenCode plugin install/remove function prototypes.
src/cli/cli.c Implements OpenCode plugin content + install/remove logic and integrates plugin/skills into install/uninstall flows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/cli/cli.c
Comment on lines +3328 to +3331
if (!g_install_plan) {
int skill_count = cbm_install_skills(skills_dir, true, dry_run);
printf(" skills: %d installed\n", skill_count);

Comment thread src/cli/cli.c
Comment on lines +1816 to +1822
#ifndef _WIN32
fchmod(fileno(f), CLI_OCTAL_PERM);
#endif
(void)fclose(f);
#ifdef _WIN32
chmod(plugin_path, CLI_OCTAL_PERM);
#endif

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14f3168029

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/cli.c
Comment on lines +549 to +550
" const payload = JSON.stringify({ tool_name: toolName, "
"tool_input: { pattern } })\n"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Pass cwd to the OpenCode hook payload

On Windows, cbm_cmd_hook_augment returns before querying unless the hook JSON contains a usable cwd (src/cli/hook_augment.c:315-334), but this OpenCode payload only sends tool_name and tool_input. As a result, every OpenCode grep/glob augmentation on Windows silently produces no context even though the installer reports the plugin as active; include the plugin context directory/cwd in the payload or spawn the child with that cwd.

Useful? React with 👍 / 👎.

Comment thread src/cli/cli.c
cbm_upsert_opencode_mcp);

if (!g_install_plan) {
int skill_count = cbm_install_skills(skills_dir, true, dry_run);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Respect --force when installing OpenCode skills

This always passes true for force, so a normal OpenCode install/update rewrites an existing ~/.config/opencode/skills/codebase-memory/SKILL.md even when the user did not request --force. The Claude path preserves existing skills unless force is set, and OpenCode should follow the same behavior to avoid clobbering local edits on routine installs.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants