Skip to content

feat: hot reload native Caplets integrations - #37

Merged
ian-pascoe merged 16 commits into
mainfrom
opencode/gentle-pixel
May 15, 2026
Merged

feat: hot reload native Caplets integrations#37
ian-pascoe merged 16 commits into
mainfrom
opencode/gentle-pixel

Conversation

@ian-pascoe

@ian-pascoeian-pascoe commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Extract a shared reloadable Caplets engine and reuse it from MCP and native integrations.
  • Add native reload/listener support, Pi runtime tool synchronization, and live OpenCode guidance for registered tools.
  • Document host-specific hot-reload behavior and add a changeset for core, Pi, and OpenCode packages.

Verification

  • pnpm verify

Summary by CodeRabbit

  • New Features

    • Native integrations hot-reload Caplets config and file edits; Pi increments tool registrations and deactivations at runtime when APIs are available.
  • Documentation

    • Updated integration guides (Native/Pi/OpenCode) detailing runtime hot-reload behavior, tool-registration limits, and fallbacks.
  • Tests

    • Added/expanded tests for engine reloads, watch-mode behavior, native service reload/notifications, Pi tool synchronization, and lifecycle edge cases.
  • Chores

    • Release metadata added and package patch bumps recorded.

Review Change Stack

@ian-pascoeian-pascoe changed the title Hot reload native Caplets integrationsfeat: hot reload native Caplets integrationsMay 15, 2026
@coderabbitai

coderabbitaiBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8e7cc913-96d6-4f46-b460-3cad3bb08f10

📥 Commits

Reviewing files that changed from the base of the PR and between 5bc496b and 4ff5605.

📒 Files selected for processing (3)
  • packages/core/test/native.test.ts
  • packages/pi/src/index.ts
  • packages/pi/test/pi.test.ts

📝 Walkthrough

Walkthrough

This PR extracts a shared CapletsEngine to centralize config reload, file watching, backend manager coordination, and tool execution. CapletsRuntime and NativeCapletsService delegate to the engine; Pi dynamically syncs Caplet tools via tool-change events; OpenCode recomputes system guidance from current registered tools. Docs, tests, and a changeset are included.

Changes

Native Hot Reload Integrations

Layer / File(s)Summary
CapletsEngine core implementation and tests
packages/core/src/engine.ts, packages/core/test/engine.test.ts, packages/core/src/native.ts
Introduces CapletsEngine with reload/watch lifecycle, backend manager coordination, execution delegation, and reload listeners. Tests cover add/remove/update caplets, reload failure retention, listener error isolation, mid-flight reloads, and watch-mode triggers.
Native service and CapletsRuntime delegation to engine
packages/core/src/native/service.ts, packages/core/src/runtime.ts, packages/core/test/native.test.ts, packages/core/test/runtime.test.ts, packages/core/test/process-cleanup.test.ts
DefaultNativeCapletsService now delegates to CapletsEngine, adding reload() and onToolsChanged(); CapletsRuntime forwards lifecycle and execution to the engine. Tests validate reload behavior, tools-changed notifications, watched updates, and process-cleanup interactions.
Pi extension dynamic tool synchronization
packages/pi/src/index.ts, packages/pi/test/pi.test.ts, packages/pi/README.md
capletsPiExtension subscribes to native service tool changes, uses signatures to detect definition changes, registers new Caplet tools, and updates/removes active Pi tools via optional APIs; unsubscribes/closes on session shutdown. Tests simulate Pi APIs and verify synchronization and lifecycle.
OpenCode system guidance refresh from live tool state
packages/opencode/src/index.ts, packages/opencode/test/opencode.test.ts, packages/opencode/README.md
createCapletsOpenCodeHooks filters system guidance at transform time using service.listTools() so guidance reflects currently registered native tools; tests added to assert guidance updates and exclusion of unregistered tools.
Implementation plan, README updates, and changeset
docs/plans/2026-05-15-native-hot-reload.md, README.md, packages/pi/README.md, packages/opencode/README.md, .changeset/native-agent-integrations.md
Adds a detailed seven-task rollout plan, updates host READMEs to document hot-reload semantics and limitations, and creates a changeset noting patch bumps and shared hot-reload runtime behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • spiritledsoftware/caplets#31: Prior native integration changes that introduced the initial native service surface and snapshot-based tool exposure; this PR refactors that surface to use a shared engine and dynamic sync.
  • spiritledsoftware/caplets#21: Related runtime/backend changes (e.g., HttpActionManager) that overlap with how CapletsEngine now coordinates backend managers and invalidation.
  • spiritledsoftware/caplets#18: Earlier hot-reload/runtime work that influenced the extraction and design of the shared engine.

Poem

🐰 I hop on code that softly hums,

A shared engine watches for the drums.
Pi and OpenCode wake and learn,
Tools refresh quick at every turn.
Hooray — new hot-reload, let's run!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% 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 nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'feat: hot reload native Caplets integrations' accurately and specifically describes the main change: introducing hot reload functionality for native Caplets integrations across core, Pi, and OpenCode packages.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck 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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch opencode/gentle-pixel

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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

@greptile-apps

greptile-appsBot commented May 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extracts a shared CapletsEngine class that centralises config loading, file watching, debounced reload, and listener notification; both the existing MCP runtime (CapletsRuntime) and the new native service (NativeCapletsService) are refactored to delegate to it. Pi and OpenCode integrations gain hot-reload support.

  • CapletsEngine adds onReload subscriptions, a serialised pending-reload loop, recursive directory-tree watchers with scheduleWatcherRefresh, and error isolation so a failing listener cannot prevent others from being called.
  • Pi integration deduplicates re-registrations via piToolSignature, pre-seeds knownCapletTools from the current active-tool list to drop stale tools immediately, and wires unsubscribe to session_shutdown.
  • OpenCode integration snapshots registered tools at plugin load; system guidance is rebuilt each request from the live service.listTools() result filtered to the initial snapshot.

Confidence Score: 5/5

Safe to merge; the reload lifecycle, watcher management, and listener isolation are all well-tested and previous review findings have been addressed.

The engine refactor is thorough and all edge cases are covered by tests. The two remaining observations are minor quality items with no impact on correctness under normal usage.

No files require special attention.

Important Files Changed

FilenameOverview
packages/core/src/engine.tsExtracts CapletsEngine with reload lifecycle, debounced watchers, recursive directory tree watching, and listener notification; logic is correct and well-tested.
packages/core/src/native/service.tsThin DefaultNativeCapletsService wrapper over CapletsEngine; all public methods delegate correctly.
packages/core/src/runtime.tsCapletsRuntime now stores unsubscribeReload and calls it in close(); reconcileTools correctly adds, updates, and removes MCP tools on each reload event.
packages/pi/src/index.tsAdds hot-reload via onToolsChanged; knownCapletTools is now pre-seeded from active tools to deactivate stale tools on first sync. Re-registration relies on Pi using the latest duplicate-name registration.
packages/opencode/src/index.tssystem.transform calls service.listTools() live filtered by a frozen registeredToolNames snapshot. Unconditional push of nativeCapletsSystemGuidance injects Caplets boilerplate even when no tools are active.

Sequence Diagram

sequenceDiagram
participant FS as FileSystem Watcher
participant Engine as CapletsEngine
participant Service as NativeCapletsService
participant Pi as Pi Extension
participant OC as OpenCode Plugin
Note over Engine: watch=true (default)
FS->>Engine: file change event
Engine->>Engine: scheduleReload() (debounced)
Engine->>Engine: reloadUntilSettled()
Engine->>Engine: reloadOnce(): load new config
Engine->>Engine: update registry + invalidate backends
Engine->>Engine: emitReload(prev, next)
par Pi Integration
Engine->>Service: onReload listener(listTools())
Service->>Pi: onToolsChanged(syncTools)
Pi->>Pi: update registeredCapletToolSignatures
Pi->>Pi: registerTool() for new/changed tools
Pi->>Pi: setActiveTools([nonCaplets, ...nextCapletTools])
and OpenCode Integration
Note over OC: No onToolsChanged subscription
OC->>Service: listTools() called live on each system.transform
OC->>OC: filter by registeredToolNames snapshot
OC->>OC: push guidance to system prompt
end
Loading

Fix All in Codex

Reviews (3): Last reviewed commit: "fix: address follow-up hot reload review..." | Re-trigger Greptile

Comment threadpackages/pi/src/index.ts
Comment threadpackages/core/src/engine.ts
Comment threadpackages/core/src/runtime.ts

@coderabbitaicoderabbitaiBot left a comment

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.

Actionable comments posted: 2

🤖 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 `@packages/pi/src/index.ts`:
- Around line 63-68: piToolSignature currently serializes only visible metadata
(title, description, promptGuidance) so changes to the backing caplet id
(caplet.caplet captured in createPiTool) are ignored and re-registration can be
skipped; update piToolSignature to include the backing caplet id (use the
caplet.caplet identifier) in the returned JSON signature so syncTools sees
changes to the execution target and triggers re-registration (refer to
piToolSignature and createPiTool to locate where the caplet id must be added).
In `@packages/pi/test/pi.test.ts`:
- Around line 396-406: The mockPiApi currently makes setActiveTools a no-op so
getActiveTools always returns the initial snapshot; modify mockPiApi so it keeps
mutable internal state (e.g., let currentActive = activeTools) and implement
setActiveTools as a vi.fn that updates that state (e.g., currentActive =
newList) and make getActiveTools return currentActive; ensure registerTool still
pushes into registered and other mocks remain vi.fn wrappers so tests see
read-after-write behavior across successive calls to
setActiveTools/getActiveTools.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: d68e59f4-3738-4580-b3f4-63fad4832a1d

📥 Commits

Reviewing files that changed from the base of the PR and between a87b539 and 5bc496b.

📒 Files selected for processing (4)
  • packages/core/src/engine.ts
  • packages/core/src/runtime.ts
  • packages/pi/src/index.ts
  • packages/pi/test/pi.test.ts

Comment threadpackages/pi/src/index.ts
Comment threadpackages/pi/test/pi.test.ts
@ian-pascoe
ian-pascoe merged commit 864feaf into mainMay 15, 2026
5 checks passed
@ian-pascoe
ian-pascoe deleted the opencode/gentle-pixel branch May 15, 2026 17:15
Sign up for freeto 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

@ian-pascoe