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

files.ts2.6 KB
// Best-effort reads of Claude's records through the scoped broker service.
// Missing and ungranted files retain the original null/empty semantics.
import * as Effect from "effect/Effect";
import * as wire from "./wire.ts";
import { Fs } from "convergence/effect";

const quiet = Effect.fnUntraced(function* <A, E extends { readonly _tag: string; readonly message: string }, R>(
  effect: Effect.Effect<A, E, R>,
) {
  return yield* effect.pipe(
    // Claude's own files are optional: unreadable history is absent history.
    Effect.catch((error) =>
      Effect.sync(() => {
        if (error._tag === "PermissionNotGranted") console.debug(`claude: ${error.message}`);
        return null;
      }),
    ),
  );
});
export function apiFiles() {
  return {
    text: Effect.fn("Claude.files.text")(function* (path: string) {
      return yield* quiet((yield* Fs).read(path));
    }),
    bytes: Effect.fn("Claude.files.bytes")(function* (path: string) {
      return yield* quiet((yield* Fs).read(path, { encoding: "binary" }));
    }),
    list: Effect.fn("Claude.files.list")(function* (path: string) {
      return yield* quiet((yield* Fs).list(path));
    }),
    stat: Effect.fn("Claude.files.stat")(function* (path: string) {
      return yield* quiet((yield* Fs).stat(path));
    }),
  };
}
export type Files = ReturnType<typeof apiFiles>;
export const readJson = Effect.fn("Claude.readJson")(function* (files: Files, path: string) {
  const text = yield* files.text(path);
  if (text === null) return null;
  try {
    const value: unknown = JSON.parse(text);
    return value;
  } catch {
    return null;
  }
});
export const readLines = Effect.fn("Claude.readLines")(function* (files: Files, path: string) {
  const text = yield* files.text(path);
  return text === null ? [] : parseLines(text);
});
export function parseLines(text: string): wire.Record[] {
  const out: wire.Record[] = [];
  for (const line of text.split("\n")) {
    if (!line.trim()) continue;
    try {
      out.push(wire.record.parse(JSON.parse(line)));
    } catch {
      /* A partially written or invalid record waits for a later read. */
    }
  }
  return out;
}
export function join(...parts: (string | null | undefined)[]) {
  return parts
    .filter((part) => part !== "" && part !== null && part !== undefined)
    .map((part, at) => (at === 0 ? String(part).replace(/\/+$/, "") : String(part).replace(/^\/+|\/+$/g, "")))
    .join("/");
}
export const mapLimit = Effect.fn("Claude.mapLimit")(function* <A, B, E, R>(
  items: A[],
  limit: number,
  work: (item: A, at: number) => Effect.Effect<B, E, R>,
) {
  return yield* Effect.forEach(items, work, { concurrency: limit });
});

Versions

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

Reviews and comments

0 threads · 0 reviews

No comments yet.