Skip to content

feat: improve native host reliability#196

Merged
MihalyToth20 merged 5 commits into
mainfrom
dev
May 11, 2026
Merged

feat: improve native host reliability#196
MihalyToth20 merged 5 commits into
mainfrom
dev

Conversation

@MihalyToth20
Copy link
Copy Markdown
Collaborator

@MihalyToth20 MihalyToth20 commented May 11, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added --oc-version CLI option to specify OpenCode versions at runtime
    • Enabled native host discovery for Chromium-based browsers with automatic extension detection
    • Added environment variable configuration support with runtime overrides for version control
  • Documentation

    • Updated oc init documentation with configuration precedence and available build-time/runtime variables
    • Enhanced oc native-host command documentation with subcommand details

Review Change Stack

Allow start/restart native-messaging commands to pass an OpenCode version override and validate version format before spawning npx. This lets the extension control tested versions while keeping the pinned default as fallback.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 11, 2026

🦋 Changeset detected

Latest commit: d85fb7a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@calycode/cli Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6614fa9d-0b42-4461-924e-35f51064e246

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR introduces native-host extension discovery for Chromium browsers, adds OpenCode version pinning with optional runtime overrides, implements platform-specific native host setup with manifest registration, and propagates version selection through server launch and native messaging.

Changes

Native Host Extension Discovery and OpenCode Version Management

Layer / File(s) Summary
Configuration & Constants
packages/cli/src/utils/host-constants.ts
HOST_APP_INFO extended with extensionDiscovery object defining trusted author/homepage patterns, native messaging permission requirements, and discovery mode.
Type Definitions
packages/cli/src/commands/opencode/native-host/discovery.ts, packages/cli/src/commands/opencode/native-host/targets.ts
New interfaces for discovery configuration, extension candidates, resolution results, and NativeHostBrowserTarget with browser, manifest path, and optional registry key.
Browser Target Resolution
packages/cli/src/commands/opencode/native-host/targets.ts
Platform-specific manifest locations and registry keys for Chrome, Brave, Edge, and Chromium on macOS, Linux, and Windows.
Extension Discovery Helpers
packages/cli/src/commands/opencode/native-host/discovery.ts
Environment variable parsing, extension ID validation, public key derivation, and discovery configuration resolution from env vars with HOST_APP_INFO fallbacks.
Extension Discovery Implementation
packages/cli/src/commands/opencode/native-host/discovery.ts
Scans Chromium user-data directories, resolves localized manifests, applies trust signals and discovery mode filtering, deduplicates by extension ID, exports resolveAllowedExtensionIds() and resolveWriteAllBrowserManifests().
Native Host Setup Workflow
packages/cli/src/commands/opencode/native-host/setup.ts
Creates platform-specific wrapper scripts, generates native messaging manifests with extension allowlists, writes manifests to browser directories, registers on Windows via registry, validates completion, and reports status with origin verification.
OpenCode Version Resolution
packages/cli/src/commands/opencode/implementation.ts
Default version constant, semver validation, and helpers to resolve version from explicit arg, --oc-version argv, CALY_OC_OPENCODE_VERSION env var, or validated default; emits warning for non-default versions.
Server Launch with Version Control
packages/cli/src/commands/opencode/implementation.ts
Updates launchOpencodeServer, proxyOpencode, serveOpencode, setupOpencode to accept and resolve ocVersion; uses resolved version in npx package specifier; extends native-host startServer/restartServer to accept version from native messages; updates CORS origins from resolveAllowedExtensionIds(); delegates showNativeHostStatus to implementation.
CLI Command Wiring
packages/cli/src/commands/opencode/index.ts
Adds --oc-version <version> option to oc/opencode command; threads ocVersion into setupOpencode and serveOpencode calls; extends proxy run command to parse --oc-version from argv and pass to proxyOpencode.
Build Configuration
packages/cli/esbuild.config.ts
Adds defineEnvValue helper to inline environment variables as esbuild literals; computes buildEnvDefines from CALY build-time env vars; passes define option to esbuild for env var inlining.
Bundled CLI Integration
packages/cli/src/index-bundled.ts
Removes HOST_APP_INFO import; updates setup messaging to reference deterministic extension discovery instead of extension count.
Documentation & Release Notes
.changeset/calm-trains-wash.md, docs/commands/oc-init.md, docs/commands/oc-native-host.md
Adds Changesets patch entry; documents native host extension discovery configuration precedence and CALY_BUILD_OC_* / CALY_OC_* variables; documents oc native-host status subcommand.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • calycode/xano-tools#194: Refactors native-host and server startup logic in implementation.ts, including updates to showNativeHostStatus and centralization of server launch paths.

Poem

🐰 A rabbit hops through Chromium lands,
Discovering extensions with nimble hands,
With versioned hosts and trusted ID schemes,
The native messaging flows in dreams,
Setup, launch, and CORS align—
A native host that's simply divine! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'improve native host reliability' accurately summarizes the main objective of this changeset, which focuses on stabilizing the OpenCode native host with improved setup, discovery, and version control mechanisms.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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
Contributor

@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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/cli/src/commands/opencode/implementation.ts (1)

1437-1463: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

ocVersion is validated but never propagated by setupOpencode.

setupOpencode({ ocVersion }) resolves and warns about the version, but the resolved value isn't forwarded to setupNativeHostRegistration or persisted anywhere; the wrapper script emitted in setup.ts doesn't include --oc-version, and the native-host's fallback parser (parseOcVersionFromArgv(process.argv) at line 623) reads from the host process's own argv. Net effect: caly-xano oc init --oc-version 1.2.3 validates the input and then silently throws it away — every subsequent server start will resolve to the default (or whatever the extension passes).

If --oc-version on init is meant to pin the version for the installed native host, bake it into the wrapper script (e.g. append --oc-version <value> to the generated .bat/.sh); otherwise remove the option from oc init to avoid misleading users.

🤖 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 `@packages/cli/src/commands/opencode/implementation.ts` around lines 1437 -
1463, setupOpencode currently resolves ocVersion but doesn't pass it on or bake
it into the generated wrapper, so init validates but discards the value; modify
setupOpencode to forward the resolvedVersion into setupNativeHostRegistration
(e.g. change call to setupNativeHostRegistration(extensionIds, resolvedVersion))
and update the wrapper-generation logic in setup.ts to append `--oc-version
<resolvedVersion>` into the emitted .sh/.bat commands so the native host
receives the pinned version (this ensures parseOcVersionFromArgv(process.argv)
will see the value). If the CLI should not support persisting ocVersion, instead
remove the ocVersion option from setupOpencode and callers to avoid the
misleading behavior.
packages/cli/src/commands/opencode/index.ts (1)

41-47: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

init and serve actions do not receive parent --oc-version option — they declare only (options) and try to read options.ocVersion.

In Commander.js v14.0.0, action handler callbacks receive (options, command) where options contains only that subcommand's own parsed options. Parent/global options must be accessed via command.parent?.opts() or command.optsWithGlobals(). The --oc-version option is registered on the oc parent command (line 32), but the init (lines 41–47) and serve (lines 174–180) actions only declare the options parameter and attempt to read options.ocVersion, which will always be undefined.

Result: caly-xano oc init --oc-version 1.14.41 and caly-xano oc serve --oc-version 1.14.41 both pass ocVersion: undefined to their handlers, silently falling back to defaults. The run action (line 206) already demonstrates the correct pattern with command.parent?.opts()?.ocVersion.

Add the command parameter to both action callbacks and access parent options via command.parent?.opts()?.ocVersion or command.optsWithGlobals().ocVersion.

Proposed fix using command.parent?.opts()
    opencodeNamespace
       .command('init')
       .description(
          'Initialize OpenCode native host integration and configuration for use with the CalyCode extension.',
       )
       .option('-f, --force', 'Force overwrite existing configuration files')
       .option('--skip-config', 'Skip installing OpenCode configuration templates')
-      .action(async (options) => {
+      .action(async (options, command) => {
          await setupOpencode({
             force: options.force,
             skipConfig: options.skipConfig,
-            ocVersion: options.ocVersion,
+            ocVersion: command.parent?.opts()?.ocVersion,
          });
       });
@@
    opencodeNamespace
       .command('serve')
       .description('Serve the OpenCode AI server locally.')
       .option('--port <port>', 'Port to run the OpenCode server on (default: 4096)')
       .option('-d, --detach', 'Run the server in the background (detached mode)')
-      .action(async (options) => {
+      .action(async (options, command) => {
          await serveOpencode({
             port: options.port ? parseInt(options.port, 10) : undefined,
             detach: options.detach,
-            ocVersion: options.ocVersion,
+            ocVersion: command.parent?.opts()?.ocVersion,
          });
       });
🤖 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 `@packages/cli/src/commands/opencode/index.ts` around lines 41 - 47, The init
and serve action handlers call setupOpencode({ ocVersion: options.ocVersion })
but only declare (options) so they never see the parent --oc-version; change
both action callbacks to accept the second parameter (commonly named command)
and read the parent/global option via command.parent?.opts()?.ocVersion (or
command.optsWithGlobals().ocVersion) when passing ocVersion into setupOpencode
and similar calls so the parent oc command's --oc-version value is used.
🧹 Nitpick comments (7)
packages/cli/src/utils/host-constants.ts (1)

22-22: ⚡ Quick win

Use proper type definition instead of inline type cast.

The type cast as 'strict' | 'balanced' | 'name-only' is a workaround. Consider defining a proper type for the mode:

♻️ Proposed improvement
+type ExtensionDiscoveryMode = 'strict' | 'balanced' | 'name-only';
+
 export const HOST_APP_INFO = {
    name: 'CalyCode Xano CLI',
    description: 'CalyCode Xano CLI Native Host',
    reverseAppId: 'com.calycode.cli',
    appId: 'cli.calycode.com',
    version: '1.0.0',
    url: 'https://calycode.com/xano',
    // Known extension IDs (fast-path allowlist)
    extensionId: 'hadkkdmpcmllbkfopioopcmeapjchpbm',
    allowedExtensionIds: [
       'hadkkdmpcmllbkfopioopcmeapjchpbm', // Production (Chrome Web Store)
       'lnhipaeaeiegnlokhokfokndgadkohfe', // Development (unpacked)
    ],
    extensionDiscovery: {
       extensionName: '@calycode | Extension',
       trustedAuthorPatterns: ['calycode', '@calycode', 'Mihály `@calycode`'],
       trustedHomepagePrefixes: [
          'https://extension.calycode.com',
          'https://www.extension.calycode.com',
       ],
       requireNativeMessagingPermission: true,
-      mode: 'balanced' as 'strict' | 'balanced' | 'name-only',
+      mode: 'balanced' as const satisfies ExtensionDiscoveryMode,
    },
 };

Note: You may also export ExtensionDiscoveryMode if it's needed elsewhere.

🤖 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 `@packages/cli/src/utils/host-constants.ts` at line 22, Replace the inline type
assertion on the "mode" value with a proper named type: define an exported type
alias (e.g. ExtensionDiscoveryMode = 'strict' | 'balanced' | 'name-only') and
use it for the mode property instead of the cast; update the constant's
declaration (the "mode" property) to be typed as ExtensionDiscoveryMode so
callers and consumers can reuse the exported type throughout the codebase.
packages/cli/src/commands/opencode/native-host/discovery.ts (1)

351-372: ⚖️ Poor tradeoff

Version selection uses modification time instead of semantic versioning.

The getLatestExtensionVersionPath function selects the "latest" extension version by sorting directories by modification time (newest first). While this may work in practice, Chrome extensions use semantic versioning in their directory names (e.g., 1.0.0_0). Consider whether semantic version comparison would be more accurate than mtime-based selection.

This is likely acceptable for production use, but worth noting that:

  • An older version directory touched more recently would be selected
  • Development scenarios with manual file changes could yield unexpected results
🤖 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 `@packages/cli/src/commands/opencode/native-host/discovery.ts` around lines 351
- 372, getLatestExtensionVersionPath currently chooses the newest directory by
mtime which can pick an older semantic version touched recently; update it to
parse semantic versions from the directory names (e.g., "1.2.3" or "1.2.3_0")
and sort using proper semver comparison (use a semver library or implement a
comparator that extracts numeric version parts) to pick the highest version from
versionDirs, and keep the mtime-based fallback only if no valid semantic version
can be parsed; reference getLatestExtensionVersionPath and the versionDirs array
when making the change.
packages/cli/src/commands/opencode/implementation.ts (1)

621-681: 💤 Low value

Indentation regression inside startServer.

The newly added try { … } block uses 4-space indentation while the rest of the file is on 3 spaces (see surrounding if (serverProc) and serverProc.on(...) calls). Formatting only — please align with the existing style so diffs/reviews aren't noisy.

🤖 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 `@packages/cli/src/commands/opencode/implementation.ts` around lines 621 - 681,
The new try block inside startServer is indented with 4 spaces, causing an
indentation regression versus the file's 3-space style; reformat the try { … }
block (including its nested lines and the catch) to use 3-space indentation to
match surrounding code (e.g., the startServer function, serverProc event
handlers, and waitForServerReady logic) so the diff only contains logical
changes.
packages/cli/src/commands/opencode/native-host/setup.ts (4)

68-82: 💤 Low value

Replace Record<string, any> with an explicit manifest type.

Per the coding guidelines, any should be avoided and object shapes defined via interfaces. The manifest schema is fixed (Chrome native messaging spec), so an explicit interface gives accurate types to both createNativeHostManifest and writeNativeHostManifests. As per coding guidelines: "Use interfaces for defining object shapes, types for unions and aliases" and "Prefer explicit typing over any type in TypeScript".

Proposed fix
+interface NativeHostManifest {
+   name: string;
+   description: string;
+   path: string;
+   type: 'stdio';
+   allowed_origins: string[];
+}
+
-function createNativeHostManifest(manifestExePath: string, allowedExtensionIds: string[]): Record<string, any> {
+function createNativeHostManifest(manifestExePath: string, allowedExtensionIds: string[]): NativeHostManifest {
@@
 function writeNativeHostManifests(
    platform: NodeJS.Platform,
    targets: NativeHostBrowserTarget[],
-   manifestContent: Record<string, any>,
+   manifestContent: NativeHostManifest,
 ): NativeHostBrowserTarget[] {
🤖 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 `@packages/cli/src/commands/opencode/native-host/setup.ts` around lines 68 -
82, Create an explicit interface (e.g., NativeHostManifest) matching the Chrome
native messaging manifest schema (fields: name: string, description: string,
path: string, type: 'stdio' | string, allowed_origins: string[] and any other
optional manifest fields), then replace the uses of Record<string, any> with
this interface: update the return type of createNativeHostManifest to
NativeHostManifest and change the manifestContent parameter type in
writeNativeHostManifests to NativeHostManifest (and adjust any related local
variables/returns to use NativeHostManifest). Ensure imports/exports are added
if needed and keep the runtime shape identical to the current implementation.

230-258: ⚡ Quick win

Status only validates the first target's manifest; other browsers' mismatches are silently ignored.

firstManifestPath = nativeHostTargets[0]?.manifestPath only reads one manifest. If browser 0 isn't installed but Chrome and Brave are, you'll either show (none) or validate a stale Chromium manifest and miss a drift in the others. Iterate over targets whose manifest exists and compare each one's allowed_origins against expectedOrigins, recommending refresh if any of them drift.

🤖 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 `@packages/cli/src/commands/opencode/native-host/setup.ts` around lines 230 -
258, The current check only reads nativeHostTargets[0]?.manifestPath
(firstManifestPath) and validates its manifestAllowedOrigins against
expectedOrigins, which misses drifts in other targets; update the logic in the
setup flow to iterate over nativeHostTargets, for each target with an existing
manifestPath read and JSON-parse its allowed_origins (like
manifestAllowedOrigins), compute missingOrigins per-target
(expectedOrigins.filter(...) against that manifest's allowed_origins), collect
any targets with missing origins, and if any exist push a descriptive line
(including target identifier e.g., target.name or manifestPath) to lines and
warn/recommend refresh (same message used now); keep the existing
extensionResolution.ids and summary lines but ensure the per-target manifest
checks replace the single-first-manifest check so no target's mismatch is
silently ignored.

142-187: 💤 Low value

Add JSDoc for the exported setupNativeHostRegistration API.

This is a public, exported entry point used from implementation.ts (and described in showNativeHostStatus flows). The coding guidelines require JSDoc on public APIs with documented parameters/return values. Same applies to showNativeHostStatus below. As per coding guidelines: "Add JSDoc comments for all public APIs and complex functions" and "Document all function parameters with types in JSDoc comments".

🤖 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 `@packages/cli/src/commands/opencode/native-host/setup.ts` around lines 142 -
187, Add a JSDoc comment for the exported function setupNativeHostRegistration
describing its purpose, the parameter extensionIds?: string[] (explain it is an
optional array of extension IDs and how it is used), and the return type
Promise<SetupNativeHostResult> (describe the manifestExePath and
extensionResolution fields). Also add matching JSDoc for the related exported
showNativeHostStatus function. Ensure the JSDoc includes parameter types,
descriptions, and a `@returns` block and place the comments immediately above each
function declaration (setupNativeHostRegistration and showNativeHostStatus) so
tooling and consumers (e.g., implementation.ts) pick up the documentation.

219-219: ⚡ Quick win

Replace execSync with spawnSync / execFileSync for the registry probe.

OpenGrep flagged the interpolated command string here. While target.registryKey originates from internal targets.ts (low risk today), the file already uses spawn with an argument array for the matching reg add call (lines 115-118); using spawnSync('reg', ['query', target.registryKey, '/ve'], …) here keeps the read path consistent and immune to future regressions if the key ever becomes user-influenced.

Proposed fix
-import { spawn, execSync } from 'node:child_process';
+import { spawn, spawnSync } from 'node:child_process';
@@
-         let registryConfigured = false;
-         try {
-            execSync(`reg query "${target.registryKey}" /ve`, { stdio: 'ignore', windowsHide: true });
-            registryConfigured = true;
-         } catch {
-            registryConfigured = false;
-         }
+         const probe = spawnSync('reg', ['query', target.registryKey, '/ve'], {
+            stdio: 'ignore',
+            windowsHide: true,
+         });
+         const registryConfigured = probe.status === 0;
🤖 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 `@packages/cli/src/commands/opencode/native-host/setup.ts` at line 219, The
current execSync call that probes the registry using an interpolated command
string should be changed to a safe argument-based child process call: replace
execSync(`reg query "${target.registryKey}" /ve`, { stdio: 'ignore',
windowsHide: true }) with a spawnSync (or execFileSync) invocation that passes
'reg' and an args array like ['query', target.registryKey, '/ve'] and the same
options ({ stdio: 'ignore', windowsHide: true }); also ensure you import/require
spawnSync from 'child_process' if not already present and follow the same
pattern used for the existing reg add spawn calls around the reg-add logic (the
lines that use spawn for reg add) to keep the registry read path consistent and
safe.
🤖 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 `@docs/commands/oc-native-host.md`:
- Around line 54-56: Remove the stray closing code fence (```) that follows the
line "Run 'caly-xano <command> --help' for detailed usage." — either delete that
trailing ``` so the two lines remain plain text, or move the "Run 'caly-xano
<command> --help'..." line into the previous `term` block (before its closing
fence) so the fenced block is properly balanced; look for the trailing
triple-backtick and the text "Run 'caly-xano <command> --help'..." to locate and
fix it.

In `@packages/cli/esbuild.config.ts`:
- Around line 10-13: The defineEnvValue helper returns the literal string
'undefined' when process.env[name] is missing; change it so missing env vars are
emitted as an empty string so downstream parsers treat them as absent—update
defineEnvValue(name: string) to return JSON.stringify(value) when present and
JSON.stringify('') (i.e., an empty string) when value === undefined; this fixes
parseListEnv('undefined') and resolveEnvWithBuildFallback returning the string
'undefined' by ensuring they receive an empty value instead.

In `@packages/cli/src/commands/opencode/native-host/discovery.ts`:
- Line 572: Add a JSDoc comment above the public function
resolveWriteAllBrowserManifests describing its purpose, behavior, inputs and
outputs: state that it takes no parameters, returns a boolean indicating whether
browser manifests should be written, describe any side effects or conditions it
checks, and include `@returns` {boolean} and a short usage example or note about
when it is used; place this comment directly above the
resolveWriteAllBrowserManifests declaration so it documents the public API.
- Around line 456-465: The balanced-mode branch (config.mode === 'balanced')
accepts if trustSignals >= 1 OR hasNativeMessagingPermission but computes
confidence solely from trustSignals, which lets an extension with 0 trustSignals
+ native messaging be accepted at low confidence; decide which behavior you want
and implement it: either (A) require at least one trust signal to accept (change
accepted to trustSignals >= 1 || (trustSignals >= 1 &&
hasNativeMessagingPermission) — i.e., remove the OR with permission), or (B)
treat native messaging as an additional trust signal by computing an
effectiveSignals value (e.g., effectiveSignals = trustSignals +
(hasNativeMessagingPermission ? 1 : 0)), then set accepted = effectiveSignals >=
1 and compute confidence from effectiveSignals (high if >=2, medium if =1, low
if 0); update the code that sets accepted and confidence accordingly
(referencing config.mode, accepted, confidence, trustSignals,
hasNativeMessagingPermission).
- Line 515: Add a JSDoc comment above the public function
resolveAllowedExtensionIds describing its purpose, the providedIds parameter
(type and optional nature) and the structure of the ResolveExtensionIdsResult
return value; include `@param` for providedIds and `@returns` describing what
keys/fields the ResolveExtensionIdsResult contains and any behavior (e.g.,
defaults when providedIds is undefined) so the public API is properly documented
for consumers and tooling.

In `@packages/cli/src/commands/opencode/native-host/setup.ts`:
- Around line 23-47: The two unconditional Windows warnings should only show for
non-bundled (dev) runs: move the log.warn calls into the else branch that
handles !isBundled so they only execute when using the batch wrapper in
development. Update the code around isBundled (the branch that builds
wrapperContent for process.execPath vs process.argv[1]) to emit log.warn('Note:
Development mode on Windows uses a batch file wrapper.') and log.warn('If Native
Messaging fails, try building the bundled exe instead.') inside the else branch,
leaving wrapperPath creation, fs.writeFileSync, and the success log.info calls
as-is; ensure wrapperPath is still returned for both branches.

---

Outside diff comments:
In `@packages/cli/src/commands/opencode/implementation.ts`:
- Around line 1437-1463: setupOpencode currently resolves ocVersion but doesn't
pass it on or bake it into the generated wrapper, so init validates but discards
the value; modify setupOpencode to forward the resolvedVersion into
setupNativeHostRegistration (e.g. change call to
setupNativeHostRegistration(extensionIds, resolvedVersion)) and update the
wrapper-generation logic in setup.ts to append `--oc-version <resolvedVersion>`
into the emitted .sh/.bat commands so the native host receives the pinned
version (this ensures parseOcVersionFromArgv(process.argv) will see the value).
If the CLI should not support persisting ocVersion, instead remove the ocVersion
option from setupOpencode and callers to avoid the misleading behavior.

In `@packages/cli/src/commands/opencode/index.ts`:
- Around line 41-47: The init and serve action handlers call setupOpencode({
ocVersion: options.ocVersion }) but only declare (options) so they never see the
parent --oc-version; change both action callbacks to accept the second parameter
(commonly named command) and read the parent/global option via
command.parent?.opts()?.ocVersion (or command.optsWithGlobals().ocVersion) when
passing ocVersion into setupOpencode and similar calls so the parent oc
command's --oc-version value is used.

---

Nitpick comments:
In `@packages/cli/src/commands/opencode/implementation.ts`:
- Around line 621-681: The new try block inside startServer is indented with 4
spaces, causing an indentation regression versus the file's 3-space style;
reformat the try { … } block (including its nested lines and the catch) to use
3-space indentation to match surrounding code (e.g., the startServer function,
serverProc event handlers, and waitForServerReady logic) so the diff only
contains logical changes.

In `@packages/cli/src/commands/opencode/native-host/discovery.ts`:
- Around line 351-372: getLatestExtensionVersionPath currently chooses the
newest directory by mtime which can pick an older semantic version touched
recently; update it to parse semantic versions from the directory names (e.g.,
"1.2.3" or "1.2.3_0") and sort using proper semver comparison (use a semver
library or implement a comparator that extracts numeric version parts) to pick
the highest version from versionDirs, and keep the mtime-based fallback only if
no valid semantic version can be parsed; reference getLatestExtensionVersionPath
and the versionDirs array when making the change.

In `@packages/cli/src/commands/opencode/native-host/setup.ts`:
- Around line 68-82: Create an explicit interface (e.g., NativeHostManifest)
matching the Chrome native messaging manifest schema (fields: name: string,
description: string, path: string, type: 'stdio' | string, allowed_origins:
string[] and any other optional manifest fields), then replace the uses of
Record<string, any> with this interface: update the return type of
createNativeHostManifest to NativeHostManifest and change the manifestContent
parameter type in writeNativeHostManifests to NativeHostManifest (and adjust any
related local variables/returns to use NativeHostManifest). Ensure
imports/exports are added if needed and keep the runtime shape identical to the
current implementation.
- Around line 230-258: The current check only reads
nativeHostTargets[0]?.manifestPath (firstManifestPath) and validates its
manifestAllowedOrigins against expectedOrigins, which misses drifts in other
targets; update the logic in the setup flow to iterate over nativeHostTargets,
for each target with an existing manifestPath read and JSON-parse its
allowed_origins (like manifestAllowedOrigins), compute missingOrigins per-target
(expectedOrigins.filter(...) against that manifest's allowed_origins), collect
any targets with missing origins, and if any exist push a descriptive line
(including target identifier e.g., target.name or manifestPath) to lines and
warn/recommend refresh (same message used now); keep the existing
extensionResolution.ids and summary lines but ensure the per-target manifest
checks replace the single-first-manifest check so no target's mismatch is
silently ignored.
- Around line 142-187: Add a JSDoc comment for the exported function
setupNativeHostRegistration describing its purpose, the parameter extensionIds?:
string[] (explain it is an optional array of extension IDs and how it is used),
and the return type Promise<SetupNativeHostResult> (describe the manifestExePath
and extensionResolution fields). Also add matching JSDoc for the related
exported showNativeHostStatus function. Ensure the JSDoc includes parameter
types, descriptions, and a `@returns` block and place the comments immediately
above each function declaration (setupNativeHostRegistration and
showNativeHostStatus) so tooling and consumers (e.g., implementation.ts) pick up
the documentation.
- Line 219: The current execSync call that probes the registry using an
interpolated command string should be changed to a safe argument-based child
process call: replace execSync(`reg query "${target.registryKey}" /ve`, { stdio:
'ignore', windowsHide: true }) with a spawnSync (or execFileSync) invocation
that passes 'reg' and an args array like ['query', target.registryKey, '/ve']
and the same options ({ stdio: 'ignore', windowsHide: true }); also ensure you
import/require spawnSync from 'child_process' if not already present and follow
the same pattern used for the existing reg add spawn calls around the reg-add
logic (the lines that use spawn for reg add) to keep the registry read path
consistent and safe.

In `@packages/cli/src/utils/host-constants.ts`:
- Line 22: Replace the inline type assertion on the "mode" value with a proper
named type: define an exported type alias (e.g. ExtensionDiscoveryMode =
'strict' | 'balanced' | 'name-only') and use it for the mode property instead of
the cast; update the constant's declaration (the "mode" property) to be typed as
ExtensionDiscoveryMode so callers and consumers can reuse the exported type
throughout the codebase.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a5e72690-a7b0-477c-b9d2-af26447559b1

📥 Commits

Reviewing files that changed from the base of the PR and between a2d0e9c and 29e4a1e.

📒 Files selected for processing (11)
  • .changeset/calm-trains-wash.md
  • docs/commands/oc-init.md
  • docs/commands/oc-native-host.md
  • packages/cli/esbuild.config.ts
  • packages/cli/src/commands/opencode/implementation.ts
  • packages/cli/src/commands/opencode/index.ts
  • packages/cli/src/commands/opencode/native-host/discovery.ts
  • packages/cli/src/commands/opencode/native-host/setup.ts
  • packages/cli/src/commands/opencode/native-host/targets.ts
  • packages/cli/src/index-bundled.ts
  • packages/cli/src/utils/host-constants.ts

Comment thread docs/commands/oc-native-host.md Outdated
Comment on lines +54 to +56
Run 'caly-xano <command> --help' for detailed usage.
https://github.com/calycode/xano-tools | https://links.calycode.com/discord
``` No newline at end of file
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove extraneous closing code fence.

Lines 54-56 appear to have a stray closing code fence at line 56. The content "Run 'caly-xano..." typically appears inside documentation sections, not as a separate fenced block. Either:

  • Remove the closing ``` at line 56 if lines 54-55 should be plain text, or
  • Move lines 54-55 inside the previous term block (before line 52's closing fence)
📝 Proposed fix
 Run 'caly-xano <command> --help' for detailed usage.
 https://github.com/calycode/xano-tools | https://links.calycode.com/discord
-```
📝 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
Run 'caly-xano <command> --help' for detailed usage.
https://github.com/calycode/xano-tools | https://links.calycode.com/discord
```
\ No newline at end of file
```
Run 'caly-xano <command> --help' for detailed usage.
https://github.com/calycode/xano-tools | https://links.calycode.com/discord
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

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

(MD040, fenced-code-language)

🤖 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/commands/oc-native-host.md` around lines 54 - 56, Remove the stray
closing code fence (```) that follows the line "Run 'caly-xano <command> --help'
for detailed usage." — either delete that trailing ``` so the two lines remain
plain text, or move the "Run 'caly-xano <command> --help'..." line into the
previous `term` block (before its closing fence) so the fenced block is properly
balanced; look for the trailing triple-backtick and the text "Run 'caly-xano
<command> --help'..." to locate and fix it.

Comment on lines +10 to +13
function defineEnvValue(name: string): string {
const value = process.env[name];
return value === undefined ? 'undefined' : JSON.stringify(value);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how discovery.ts handles potentially 'undefined' string values

rg -n -A5 -B2 'BUILD_OC_DEFAULTS\.' packages/cli/src/commands/opencode/native-host/discovery.ts

Repository: calycode/xano-tools

Length of output: 2742


🏁 Script executed:

#!/bin/bash
# Get context around defineEnvValue function
cat -n packages/cli/esbuild.config.ts | head -30

Repository: calycode/xano-tools

Length of output: 1921


🏁 Script executed:

#!/bin/bash
# Find resolveEnvWithBuildFallback and related helpers
rg -n "resolveEnvWithBuildFallback|parseListEnvWithBuildFallback|parseBooleanEnvWithBuildFallback|defineEnvValue" packages/cli/src/commands/opencode/native-host/discovery.ts | head -20

Repository: calycode/xano-tools

Length of output: 976


🏁 Script executed:

#!/bin/bash
# Find where defineEnvValue is defined and used
rg -n "defineEnvValue" packages/cli/

Repository: calycode/xano-tools

Length of output: 1563


🏁 Script executed:

#!/bin/bash
# Get resolveEnvWithBuildFallback implementation
sed -n '88,100p' packages/cli/src/commands/opencode/native-host/discovery.ts

Repository: calycode/xano-tools

Length of output: 399


🏁 Script executed:

#!/bin/bash
# Find BUILD_OC_DEFAULTS definition
rg -n "BUILD_OC_DEFAULTS\s*=" packages/cli/src/commands/opencode/native-host/discovery.ts -A 20

Repository: calycode/xano-tools

Length of output: 1020


🏁 Script executed:

#!/bin/bash
# Get parseBooleanEnvWithBuildFallback and parseListEnvWithBuildFallback implementations
sed -n '102,125p' packages/cli/src/commands/opencode/native-host/discovery.ts

Repository: calycode/xano-tools

Length of output: 721


🏁 Script executed:

#!/bin/bash
# Check if string 'undefined' is handled specially anywhere in discovery.ts
rg -n "'undefined'|\"undefined\"" packages/cli/src/commands/opencode/native-host/discovery.ts

Repository: calycode/xano-tools

Length of output: 45


🏁 Script executed:

#!/bin/bash
# Find parseBooleanEnv and parseListEnv functions
rg -n "function parseBooleanEnv|function parseListEnv" packages/cli/src/commands/opencode/native-host/discovery.ts -A 10

Repository: calycode/xano-tools

Length of output: 1599


🏁 Script executed:

#!/bin/bash
# Check imports to see if these parsing functions come from elsewhere
head -30 packages/cli/src/commands/opencode/native-host/discovery.ts

Repository: calycode/xano-tools

Length of output: 1213


🏁 Script executed:

#!/bin/bash
# See how parseListEnvWithBuildFallback results are used - specifically the trusted* patterns
sed -n '168,190p' packages/cli/src/commands/opencode/native-host/discovery.ts

Repository: calycode/xano-tools

Length of output: 967


🏁 Script executed:

#!/bin/bash
# Check if there are any filters for 'undefined' string value anywhere
rg -n "isValidExtensionId|filter|Boolean" packages/cli/src/commands/opencode/native-host/discovery.ts | head -20

Repository: calycode/xano-tools

Length of output: 1131


Fix defineEnvValue to properly indicate missing environment variables.

The function returns the string 'undefined' when an environment variable is missing, but downstream parsing functions treat non-empty strings as valid values. This causes parseListEnv('undefined') to return ['undefined'] and resolveEnvWithBuildFallback to return the string 'undefined' instead of undefined, bypassing intended fallbacks.

Return an empty string '' instead of 'undefined' when a variable is missing. This allows existing parsing functions to correctly treat it as absent.

🤖 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 `@packages/cli/esbuild.config.ts` around lines 10 - 13, The defineEnvValue
helper returns the literal string 'undefined' when process.env[name] is missing;
change it so missing env vars are emitted as an empty string so downstream
parsers treat them as absent—update defineEnvValue(name: string) to return
JSON.stringify(value) when present and JSON.stringify('') (i.e., an empty
string) when value === undefined; this fixes parseListEnv('undefined') and
resolveEnvWithBuildFallback returning the string 'undefined' by ensuring they
receive an empty value instead.

Comment on lines +456 to +465
if (config.mode === 'name-only') {
accepted = true;
confidence = 'low';
} else if (config.mode === 'strict') {
accepted = trustSignals >= 2;
confidence = accepted ? 'high' : 'low';
} else {
accepted = trustSignals >= 1 || hasNativeMessagingPermission;
confidence = trustSignals >= 2 ? 'high' : trustSignals >= 1 ? 'medium' : 'low';
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Clarify balanced mode acceptance criteria for extensions with only native messaging permission.

In balanced mode (lines 463-464), an extension is accepted if it has either 1+ trust signals or the native messaging permission, but the confidence is assigned based only on trust signal count. This means:

  • An extension with 0 trust signals but native messaging permission is accepted with low confidence
  • This is more permissive than requiring at least one trust signal

Confirm this behavior is intentional. If the native messaging permission alone should not be sufficient for acceptance, consider:

-            accepted = trustSignals >= 1 || hasNativeMessagingPermission;
+            accepted = trustSignals >= 1;
             confidence = trustSignals >= 2 ? 'high' : trustSignals >= 1 ? 'medium' : 'low';

Alternatively, if the permission should count as a trust signal, adjust the confidence calculation accordingly.

🤖 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 `@packages/cli/src/commands/opencode/native-host/discovery.ts` around lines 456
- 465, The balanced-mode branch (config.mode === 'balanced') accepts if
trustSignals >= 1 OR hasNativeMessagingPermission but computes confidence solely
from trustSignals, which lets an extension with 0 trustSignals + native
messaging be accepted at low confidence; decide which behavior you want and
implement it: either (A) require at least one trust signal to accept (change
accepted to trustSignals >= 1 || (trustSignals >= 1 &&
hasNativeMessagingPermission) — i.e., remove the OR with permission), or (B)
treat native messaging as an additional trust signal by computing an
effectiveSignals value (e.g., effectiveSignals = trustSignals +
(hasNativeMessagingPermission ? 1 : 0)), then set accepted = effectiveSignals >=
1 and compute confidence from effectiveSignals (high if >=2, medium if =1, low
if 0); update the code that sets accepted and confidence accordingly
(referencing config.mode, accepted, confidence, trustSignals,
hasNativeMessagingPermission).

};
}

function resolveAllowedExtensionIds(providedIds?: string[]): ResolveExtensionIdsResult {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add JSDoc documentation for public API function.

The resolveAllowedExtensionIds function is a public API but lacks JSDoc comments. According to coding guidelines, all public APIs must have JSDoc documentation including parameter and return value descriptions.

📝 Proposed documentation
+/**
+ * Resolve the list of allowed extension IDs for the native host.
+ * 
+ * `@param` providedIds - Optional array of manually specified extension IDs. If provided, 
+ *                      these IDs are validated and returned without discovery.
+ * `@returns` Result object containing the resolved extension IDs, matched candidates, 
+ *          and the source of the IDs (manual, discovery mode, or discovery+known).
+ * `@throws` Error if any provided ID is invalid (must match Chrome extension ID format).
+ */
 function resolveAllowedExtensionIds(providedIds?: string[]): ResolveExtensionIdsResult {

As per coding guidelines, public APIs must be documented with JSDoc comments including parameter and return value descriptions.

🤖 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 `@packages/cli/src/commands/opencode/native-host/discovery.ts` at line 515, Add
a JSDoc comment above the public function resolveAllowedExtensionIds describing
its purpose, the providedIds parameter (type and optional nature) and the
structure of the ResolveExtensionIdsResult return value; include `@param` for
providedIds and `@returns` describing what keys/fields the
ResolveExtensionIdsResult contains and any behavior (e.g., defaults when
providedIds is undefined) so the public API is properly documented for consumers
and tooling.

};
}

function resolveWriteAllBrowserManifests(): boolean {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add JSDoc documentation for public API function.

The resolveWriteAllBrowserManifests function is a public API but lacks JSDoc comments.

📝 Proposed documentation
+/**
+ * Resolve whether to write native host manifests for all supported browsers.
+ * 
+ * `@returns` true if manifests should be written for all browsers, false otherwise.
+ *          Reads from CALY_OC_WRITE_ALL_BROWSER_MANIFESTS or CALY_BUILD_OC_WRITE_ALL_BROWSER_MANIFESTS.
+ */
 function resolveWriteAllBrowserManifests(): boolean {

As per coding guidelines, public APIs must be documented with JSDoc comments.

📝 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
function resolveWriteAllBrowserManifests(): boolean {
/**
* Resolve whether to write native host manifests for all supported browsers.
*
* `@returns` true if manifests should be written for all browsers, false otherwise.
* Reads from CALY_OC_WRITE_ALL_BROWSER_MANIFESTS or CALY_BUILD_OC_WRITE_ALL_BROWSER_MANIFESTS.
*/
function resolveWriteAllBrowserManifests(): boolean {
🤖 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 `@packages/cli/src/commands/opencode/native-host/discovery.ts` at line 572, Add
a JSDoc comment above the public function resolveWriteAllBrowserManifests
describing its purpose, behavior, inputs and outputs: state that it takes no
parameters, returns a boolean indicating whether browser manifests should be
written, describe any side effects or conditions it checks, and include `@returns`
{boolean} and a short usage example or note about when it is used; place this
comment directly above the resolveWriteAllBrowserManifests declaration so it
documents the public API.

Comment on lines +23 to +47
if (isWin) {
const wrapperDir = path.join(homeDir, '.calycode', 'bin');
if (!fs.existsSync(wrapperDir)) {
fs.mkdirSync(wrapperDir, { recursive: true });
}

const wrapperPath = path.join(wrapperDir, 'calycode-host.bat');
let wrapperContent = '';
const isBundled = process.execPath.toLowerCase().endsWith('caly.exe') || (process as any).pkg;

if (isBundled) {
wrapperContent = `@echo off\r\n`;
wrapperContent += `"${process.execPath}" opencode native-host %*\r\n`;
} else {
wrapperContent = `@echo off\r\n`;
wrapperContent += `"${process.execPath}" "${process.argv[1]}" opencode native-host %*\r\n`;
}

fs.writeFileSync(wrapperPath, wrapperContent);
log.info(`Created wrapper script: ${wrapperPath}`);
log.info(`Wrapper content: ${wrapperContent.trim()}`);
log.warn('Note: Development mode on Windows uses a batch file wrapper.');
log.warn('If Native Messaging fails, try building the bundled exe instead.');
return wrapperPath;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Dev-mode warning is shown unconditionally on Windows, even for bundled installs.

log.warn('Note: Development mode on Windows uses a batch file wrapper.') and the follow-up suggestion to "try building the bundled exe instead" fire after both the isBundled and else branches, so users running the bundled caly.exe are incorrectly told they're in development mode and prompted to rebuild. Move these warnings inside the else (non-bundled) branch.

Proposed fix
       if (isBundled) {
          wrapperContent = `@echo off\r\n`;
          wrapperContent += `"${process.execPath}" opencode native-host %*\r\n`;
       } else {
          wrapperContent = `@echo off\r\n`;
          wrapperContent += `"${process.execPath}" "${process.argv[1]}" opencode native-host %*\r\n`;
       }

       fs.writeFileSync(wrapperPath, wrapperContent);
       log.info(`Created wrapper script: ${wrapperPath}`);
       log.info(`Wrapper content: ${wrapperContent.trim()}`);
-      log.warn('Note: Development mode on Windows uses a batch file wrapper.');
-      log.warn('If Native Messaging fails, try building the bundled exe instead.');
+      if (!isBundled) {
+         log.warn('Note: Development mode on Windows uses a batch file wrapper.');
+         log.warn('If Native Messaging fails, try building the bundled exe instead.');
+      }
       return wrapperPath;
📝 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
if (isWin) {
const wrapperDir = path.join(homeDir, '.calycode', 'bin');
if (!fs.existsSync(wrapperDir)) {
fs.mkdirSync(wrapperDir, { recursive: true });
}
const wrapperPath = path.join(wrapperDir, 'calycode-host.bat');
let wrapperContent = '';
const isBundled = process.execPath.toLowerCase().endsWith('caly.exe') || (process as any).pkg;
if (isBundled) {
wrapperContent = `@echo off\r\n`;
wrapperContent += `"${process.execPath}" opencode native-host %*\r\n`;
} else {
wrapperContent = `@echo off\r\n`;
wrapperContent += `"${process.execPath}" "${process.argv[1]}" opencode native-host %*\r\n`;
}
fs.writeFileSync(wrapperPath, wrapperContent);
log.info(`Created wrapper script: ${wrapperPath}`);
log.info(`Wrapper content: ${wrapperContent.trim()}`);
log.warn('Note: Development mode on Windows uses a batch file wrapper.');
log.warn('If Native Messaging fails, try building the bundled exe instead.');
return wrapperPath;
}
if (isWin) {
const wrapperDir = path.join(homeDir, '.calycode', 'bin');
if (!fs.existsSync(wrapperDir)) {
fs.mkdirSync(wrapperDir, { recursive: true });
}
const wrapperPath = path.join(wrapperDir, 'calycode-host.bat');
let wrapperContent = '';
const isBundled = process.execPath.toLowerCase().endsWith('caly.exe') || (process as any).pkg;
if (isBundled) {
wrapperContent = `@echo off\r\n`;
wrapperContent += `"${process.execPath}" opencode native-host %*\r\n`;
} else {
wrapperContent = `@echo off\r\n`;
wrapperContent += `"${process.execPath}" "${process.argv[1]}" opencode native-host %*\r\n`;
}
fs.writeFileSync(wrapperPath, wrapperContent);
log.info(`Created wrapper script: ${wrapperPath}`);
log.info(`Wrapper content: ${wrapperContent.trim()}`);
if (!isBundled) {
log.warn('Note: Development mode on Windows uses a batch file wrapper.');
log.warn('If Native Messaging fails, try building the bundled exe instead.');
}
return wrapperPath;
}
🤖 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 `@packages/cli/src/commands/opencode/native-host/setup.ts` around lines 23 -
47, The two unconditional Windows warnings should only show for non-bundled
(dev) runs: move the log.warn calls into the else branch that handles !isBundled
so they only execute when using the batch wrapper in development. Update the
code around isBundled (the branch that builds wrapperContent for
process.execPath vs process.argv[1]) to emit log.warn('Note: Development mode on
Windows uses a batch file wrapper.') and log.warn('If Native Messaging fails,
try building the bundled exe instead.') inside the else branch, leaving
wrapperPath creation, fs.writeFileSync, and the success log.info calls as-is;
ensure wrapperPath is still returned for both branches.

@MihalyToth20
Copy link
Copy Markdown
Collaborator Author

Addressed the review feedback in this branch and pushed commit d85fb7a.

Implemented fixes include:

  • parent --oc-version wiring for oc init/oc serve
  • persisted ocVersion through native-host setup + wrapper args
  • balanced discovery signal/confidence adjustments
  • semantic version selection for extension directories with mtime fallback
  • per-target manifest drift validation in native-host status
  • safer Windows registry probe (spawnSync instead of interpolated execSync)
  • explicit native-host manifest typing + host constant typing improvements
  • added JSDoc on exported native-host/discovery APIs
  • build env fallback now emits empty string instead of literal undefined
  • docs fence cleanup in oc-native-host.md

Validation run: pnpm --filter @calycode/cli build and pnpm --filter @calycode/cli build:js.

@MihalyToth20 MihalyToth20 merged commit 280137d into main May 11, 2026
4 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.

1 participant