Official

claude

Claude Code agent provider: runs the Claude Code CLI headless for each account.

The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/claude@0.2.0

Permissions in 0.2.0

  • Provide agents agents.provideMediumAdds agents to the app.Provide the Claude Code agent and pass its tool calls to plugin tools
  • Run named programs processMediumStarts the listed programs.Run the Claude Code CLI (sessions, sign-in, `claude update`), and ask the npm installation that owns it about a newer versionPrograms: claudenpm
  • Read files fs.readMediumReads files in the listed places.Read Claude Code's sessions, settings and skills (in ~/.claude, other accounts' ~/.claude-* folders, the shared skills and the folder you choose in Settings), the project's .claude folder, the administrator's skill policy, and once, what the previous provider keptPlaces: ~/.claude/**~/.claude*/**~/.agents/skills/**the open workspaceits own data folder/Library/Application Support/ClaudeCode/managed-settings.json/etc/claude-code/managed-settings.json${settings.configDir}
  • Environment variables envMediumReads the listed environment variables.Find Claude Code's configuration directory, and the launch settings you set in CLAUDE_* variables (extra arguments, MCP servers, setting sources, appended system prompt, extra folders)Variables: HOMECLAUDE_CONFIG_DIRCLAUDE_EXTRA_ARGSCLAUDE_MCP_CONFIGCLAUDE_STRICT_MCP_CONFIGCLAUDE_SETTING_SOURCESCLAUDE_APPEND_SYSTEM_PROMPTCLAUDE_ADD_DIRS

Files

task.ts2.1 KB
// Subagent snapshots (`TaskInfo`) as plain objects: absent fields are
// `null` or left out while the plugin works on them, and dropped when a
// snapshot is sent, the way the host's types leave them out.
import { compact } from "../sdk/agent.ts";
import { objectOf } from "./wire.ts";
import type { Task } from "./types.ts";

const LIVE = new Set(["queued", "running", "waiting"]);

/// Task statuses that still have work in hand.
export function isLive(status: string) {
  return LIVE.has(status);
}

/// The snapshot as the host reads it: no `null`s, no `background: false`,
/// no empty workflow lists.
export function cleanTask(info: Task) {
  const { workflow, background, ...rest } = info;
  const out = compact({ ...rest, background: background === true ? true : undefined });
  if (!workflow) return out;
  return {
    ...out,
    workflow: compact({
      phases: workflow.phases.length ? workflow.phases.map((phase) => ({ title: phase.title })) : undefined,
      result: workflow.result,
      logs: workflow.logs.length ? [...workflow.logs] : undefined,
    }),
  };
}

/// Whether two snapshots say the same thing.
export function sameTask(a: Task, b: Task) {
  return canonical(cleanTask(a)) === canonical(cleanTask(b));
}

function canonical(value: unknown): string {
  if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
  if (value && typeof value === "object") {
    return `{${Object.keys(value)
      .sort()
      .map((key) => `${JSON.stringify(key)}:${canonical(objectOf(value)[key])}`)
      .join(",")}}`;
  }
  return JSON.stringify(value) ?? "undefined";
}

/// A deep copy of a snapshot. (The runtime has no `structuredClone`; a
/// snapshot is JSON through and through.)
export function copyTask(info: Task): Task {
  return {
    ...info,
    usage: info.usage ? { ...info.usage } : info.usage,
    workflow: info.workflow
      ? {
          phases: info.workflow.phases.map((phase) => ({ ...phase })),
          result: JSON.parse(JSON.stringify(info.workflow.result)),
          logs: [...info.workflow.logs],
        }
      : info.workflow,
  };
}

Versions

VersionPublishedPlugin APISizePermissionsStatus
0.2.0latestOct 5, 2026>=2 <3128.7 KB4 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.