Home

mweinbach / agent-coworker

publicmweinbach/agent-coworker
Overview Code History Branches Pull requestsIssuesInsights
main
HomeOverview Code PRsIssues

[codex] Add skill and plugin turn references (#136)

4 months ago

259987f
Authored
Max Weinbach5/29/2026, 11:28:20 PM
* feat: import plugins and skills from Claude and Codex

Add a dedicated Import flow that browses and copies plugins/skills already
on disk from Claude Code (~/.claude) and Codex (~/.codex) into Cowork.

- core src/import/: external home discovery (scoped to plugins/cache,
  plugins/marketplaces, skills), plugin/skill discovery with diagnostics and
  already-installed detection, Claude .claude-plugin -> cowork conversion
  (strip commands/agents/hooks/$schema/object mcpServers), and operations that
  reuse installPluginsFromSource / installSkillsFromSource.
- WebSocket-first: cowork/import/list, cowork/import/plugin, cowork/import/skill
  JSON-RPC methods + import_list event, wired through SkillManager,
  AgentSession, and a new SessionImportService. Imports emit the standard
  plugins_catalog / skills_catalog events so every UI refreshes.
- desktop: reusable ImportDialog with a Claude/Codex toggle mounted on the
  Plugins and Skills catalog pages, with per-item Import to Global/Workspace
  and installed indicators, backed by new store state/actions.
- docs + generated jsonrpc artifacts updated; tests cover discovery,
  conversion, end-to-end import, and the JSON-RPC routes against a real server.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(desktop): make import dialog a contained scrollable list with nicer UI

The import list overflowed the dialog (items bled past the rounded
container). Follow the established PluginDetailDialog scroll pattern: make
DialogContent a bounded flex column (max-h-[85vh], gap-0, p-0, overflow-hidden)
with a fixed header and a flex-1 overflow-y-auto body so the list scrolls
inside the dialog.

Also polish the UI: icon tiles (package/sparkles), a segmented Claude/Codex
toggle, a result count, monospace ids, 2-line clamped descriptions, inline
diagnostic alerts, and per-item import spinners.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(desktop): add "Import directory" button to copy a local skill folder

Add a native folder picker on the Skills page that imports a selected
directory's skill(s) by copying (never symlinking) into Global or Workspace.

- new generic desktop IPC `pickDirectory` (no workspace-approval side effects)
  wired through preload, desktopApi, desktopSchemas, files IPC, the renderer
  command, and the web adapter (prompt fallback).
- ImportDirectoryButton: a Copy to Global / Copy to Workspace dropdown that
  picks a folder and reuses installSkills -> cowork/skills/install, which copies
  via fs.cp and writes the install manifest. Busy spinner + inline error.

Copy semantics are guaranteed by the existing skill install pipeline
(replacePluginInstallRoot + fs.cp), so e.g. ~/Desktop/diligence-report is
copied in, not linked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(desktop): move folder import into the Import dialog as a "Folder" source

Per feedback, fold the local-directory import into the Import dialog instead of
a separate toolbar button. The source toggle is now Claude / Codex / Folder.

- Folder tab: a "Choose a folder…" native picker, then Import to Global /
  Import to Workspace with busy spinners and inline error/success. Skills copy
  via installSkills; plugins via installPlugins (both copy, never symlink).
- Remove the standalone ImportDirectoryButton and its toolbar wiring; drop the
  now-unused isDesktopApiAvailable helper. The pickDirectory IPC stays and is
  now driven by the dialog.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: harden desktop ipc boundaries

* feat: add skill and plugin turn references

Add @mention reference plumbing for turn/start and turn/steer so referenced skills are force-injected before model execution and referenced plugins are rendered into turn-scoped system context.

Preserve references through desktop send, reconnect, queued-message, and workspace-default paths; enforce skill-over-plugin precedence, disabled/gated reference filtering, and the 32-reference client/server cap.

Verified with bun test --max-concurrency 1, bun run typecheck, and bun run docs:check.

* fix: resolve biome lint errors and format issues

- Fix useExhaustiveDependencies in ComposerMentionInput (remove unnecessary value dep)
- Replace array index keys with stable keys in ComposerHighlightOverlay
- Add start/end fields to text segments for stable React keys
- Format composerMentions.ts to satisfy line length
- Fix import-related type and schema issues across desktop and core
- Update synthetic skill injection tests for reference injection changes

* fix

* Update mockDesktopCommands.ts

* Update NewChatLanding.tsx

* fix: fall back after local title generation failures

* fix: harden Apple title generation

* fix: require Koffi files in Apple title bundle

* chore: organize Apple title imports

* chat/plugins/tagging

* feat: add standalone skills to the plugin marketplace

Surface marketplace-offered standalone skills alongside plugins:
- marketplace.json gains an optional `skills[]` array, parsed locally and
  remotely (reusing the GitHub tree-URL install source)
- skill catalog snapshots include `availableSkills`; the skills_catalog event
  carries `availableSkillsPartial`, mirroring the plugin marketplace flow
- SkillManager queues a remote marketplace refresh on catalog read
- desktop Skills page gains an "Available" section; install reuses
  cowork/skills/install
- regenerated JSON-RPC artifacts and documented the new fields

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: use stable mention segment keys

* fixes

* fix: refresh available skills after skill mutations

Uninstalling a marketplace skill left it absent from "Available" until a manual
refresh, because skill mutations only re-emitted the local catalog. Queue a
remote skill-marketplace refresh after skill mutations (scoped so plugin
mutations don't trigger an extra fetch) — an uninstalled skill that's still in
the marketplace now reappears as available automatically.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: deliver available marketplace skills in a single catalog emit

The async "local-first then queue remote refresh" never reached the desktop
client: the workspace-control read/refresh path captures one synchronous
skills_catalog emit and disposes the ephemeral binding, so the queued remote
emit was orphaned — an uninstalled marketplace skill never reappeared under
"Available". Emit a single marketplace-inclusive catalog from getSkillsCatalog
so both the catalog read and the post-mutation subscriber push (readRefreshEvents
-> getCatalog) carry availableSkills. Drop the dead queue machinery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: fall back to raw GitHub marketplace fetch

* Update AGENTS.md

* style: format marketplace skill test

* Fix canvas previews and preserve turn reference context

* fix: harden local import discovery

* fix: surface subagent usage accounting

* fix: derive legacy usage cost breakdowns

* fix: cap canvas preview reads

* fix: match composer mentions case-insensitively

* fix: remove stale pending thread queue helper

* style: satisfy biome check

* fix: keep usage cost derivation renderer-safe

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Parentd15cd53

120 files changed
  • AGENTS.md+5−2
  • apps/desktop/electron/ipc.ts+2−1
  • apps/desktop/electron/ipc/files.ts+29−20
  • apps/desktop/electron/ipc/mobileRelay.ts+8−3
  • apps/desktop/electron/ipc/trustedSender.ts+3−3
  • apps/desktop/electron/preload.ts+22−3
  • apps/desktop/electron/services/sidecar.ts+2−0
  • apps/desktop/src/App.tsx+1
−0
  • apps/desktop/src/app/store.actions.ts+2−0
  • apps/desktop/src/app/store.actions/import.ts+164−0
  • apps/desktop/src/app/store.actions/thread.ts+13−2
  • apps/desktop/src/app/store.actions/workspaceDefaults.ts+9−1
  • apps/desktop/src/app/store.helpers.ts+11−0
  • apps/desktop/src/app/store.helpers/controlSocket.ts+25−0
  • apps/desktop/src/app/store.helpers/extensionEvents.ts+36−3
  • apps/desktop/src/app/store.helpers/jsonRpcSocket.ts+5−0
  • apps/desktop/src/app/store.helpers/runtimeState.ts+26−9
  • apps/desktop/src/app/store.helpers/threadEventReducer/handlers/lifecycleHandlers.ts+6−3
  • apps/desktop/src/app/store.helpers/threadEventReducer/messaging.ts+18−2
  • apps/desktop/src/app/types.ts+10−0
  • apps/desktop/src/lib/desktopApi.ts+6−0
  • apps/desktop/src/lib/desktopCommands.ts+4−0
  • apps/desktop/src/lib/desktopSchemas.ts+22−0
  • apps/desktop/src/lib/webAdapter.ts+5−0
  • apps/desktop/src/lib/wsProtocol.ts+7−0
  • apps/desktop/src/ui/Canvas.tsx+20−5
  • apps/desktop/src/ui/ChatView.tsx+21−2
  • apps/desktop/src/ui/ContextSidebar.tsx+42−22
  • apps/desktop/src/ui/chat/ChatComposer.tsx+10−6
  • apps/desktop/src/ui/chat/ChatViewContext.tsx+4−0
  • apps/desktop/src/ui/chat/ComposerHighlightOverlay.tsx+48−0
  • apps/desktop/src/ui/chat/ComposerMentionInput.tsx+197−0
  • apps/desktop/src/ui/chat/ComposerMentionMenu.tsx+78−0
  • apps/desktop/src/ui/chat/FeedRow.tsx+6−3
  • apps/desktop/src/ui/chat/MentionText.tsx+83−0
  • apps/desktop/src/ui/chat/NewChatLanding.tsx+35−8
  • apps/desktop/src/ui/chat/composerMentions.ts+237−0
  • apps/desktop/src/ui/import/ImportDialog.tsx+420−0
  • apps/desktop/src/ui/layout/AppTopBar.tsx+306−22
  • apps/desktop/src/ui/plugins/PluginsCatalogPage.tsx+2−0
  • apps/desktop/src/ui/skills/AvailableSkillCardGrid.tsx+61−0
  • apps/desktop/src/ui/skills/SkillsCatalogPage.tsx+52−9
  • apps/desktop/test/app-topbar.test.tsx+201−3
  • apps/desktop/test/canvas-read-path.test.ts+18−0
  • apps/desktop/test/context-sidebar.test.tsx+23−0
  • apps/desktop/test/desktop-schemas.test.ts+55−0
  • apps/desktop/test/helpers/mockDesktopCommands.ts+1−0
  • apps/desktop/test/ipc-files.test.ts+95−0
  • apps/desktop/test/ipc-trusted-sender.test.ts+17−0
  • apps/desktop/test/jsonrpc-single-connection.test.ts+6−0
  • apps/desktop/test/mobile-relay-ipc.test.ts+40−0
  • apps/desktop/test/runtimeState.test.ts+1−2
  • apps/desktop/test/server-manager.test.ts+2−0
  • apps/desktop/test/sidecar.test.ts+7−0
  • docs/generated/websocket-jsonrpc.d.ts+29−23
  • docs/generated/websocket-jsonrpc.schema.json+1−1
  • docs/websocket-protocol.md+114−2
  • scripts/build_desktop_resources.ts+4−0
  • src/agent.ts+16−2
  • src/import/conversion.ts+66−0
  • src/import/discovery.ts+382−0
  • src/import/externalHomes.ts+79−0
  • src/import/index.ts+21−0
  • src/import/operations.ts+94−0
  • src/plugins/manifest.ts+12−2
  • src/plugins/marketplace.ts+96−49
  • src/plugins/remoteMarketplace.ts+105−4
  • src/server/agents/AgentControl.ts+6−1
  • src/server/jsonrpc/routes/import.ts+115−0
  • src/server/jsonrpc/routes/index.ts+2−0
  • src/server/jsonrpc/routes/turn.ts+4−2
  • src/server/jsonrpc/schema.import.ts+16−0
  • src/server/jsonrpc/schema.threadTurn.ts+11−0
  • src/server/jsonrpc/schema.ts+3−0
  • src/server/protocol.ts+10−0
  • src/server/runtime/SessionRegistry.ts+4−0
  • src/server/session/AgentSession.ts+30−0
  • src/server/session/SessionAdminManager.ts+3−0
  • src/server/session/SessionContext.ts+15−0
  • src/server/session/SessionRuntime.ts+42−2
  • src/server/session/SkillManager.ts+103−3
  • src/server/session/TurnExecutionManager.ts+5−0
  • src/server/session/turnExecution/referenceInjection.ts+115−0
  • src/server/session/turnExecution/runTurnInvocation.ts+1−0
  • src/server/session/turnExecution/runUserMessageTurn.ts+32−2
  • src/server/session/turnExecution/steerCoordinator.ts+112−13
  • src/server/session/turnExecution/userMessageAttachments.ts+17−2
  • src/server/title/appleFoundationTitle.ts+36−7
  • src/server/title/index.ts+4−15
  • src/session/costTracker.ts+252−8
  • src/session/pricing.ts+34−1
  • src/session/sessionUsageSchema.ts+14−0
  • src/sessionContext/renderReferencedPluginsSection.ts+40−0
  • src/shared/agents.ts+4−0
  • src/shared/jsonrpcControlSchemas.ts+90−0
  • src/skills/catalog.ts+2−0
  • src/skills/loadSkillBody.ts+88−0
  • src/skills/operations.ts+33−2
  • src/tools/skill.ts+5−54
  • src/turnSystemPrompt.ts+8−1
  • src/types.ts+53−0
  • test/agentControl.test.ts+18−1
  • test/build-desktop-resources.test.ts+68−0
  • test/desktop.composerMentions.test.ts+205−0
  • test/desktop.pendingThreadReferences.test.ts+50−0
  • test/import.test.ts+352−0
  • test/jsonrpc/control.import.test.ts+136−0
  • test/server.jsonrpc.turnReferences.schema.test.ts+88−0
  • test/session-title-service.test.ts+108−10
  • test/session.costTracker.test.ts+139−1
  • test/session.managers.test.ts+35−0
  • test/session.syntheticSkillInjection.test.ts+302−0
  • test/session/agentSession.agent-events.test.ts+108−0
  • test/session/agentSession.harness.ts+2−0
  • test/session/agentSession.messaging.test.ts+271−0
  • test/session/agentSession.skills-marketplace.test.ts+103−0
  • test/session/pricing.test.ts+15−0
  • test/sessionContext.renderReferencedPluginsSection.test.ts+47−0
  • test/skills.loadSkillBody.test.ts+104−0
  • test/skills.marketplace.test.ts+280−0