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
Files
config.test.ts5 KB
// Ported from the Rust plugin's config.rs and instances.rs tests.
import assert from "node:assert/strict";
import { test } from "./testing.ts";
import {
childEnv,
configDirOf,
continuationKey,
emptyLaunch,
launchArgs,
launchFromEnv,
splitArgs,
workflowSettings,
} from "./config.ts";
import { agentId, defaultAccount, displayName, parseInstances } from "./instances.ts";
test("the continuation key is empty for the default directory", () => {
assert.equal(continuationKey(defaultAccount(), { HOME: "/h" }), "");
assert.equal(
continuationKey(defaultAccount(), { HOME: "/h", CLAUDE_CONFIG_DIR: "/h/.claude_work" }),
"/h/.claude_work",
);
assert.equal(configDirOf(defaultAccount(), { HOME: "/h" }), "/h/.claude");
assert.equal(configDirOf(defaultAccount(), { HOME: "/h", CLAUDE_CONFIG_DIR: "/h/.claude_work" }), "/h/.claude_work");
});
/// Exporting `CLAUDE_CONFIG_DIR`, even as `~/.claude`, switches the CLI off
/// the keychain and it answers `loggedIn: false`.
test("the default account does not export a config dir", () => {
assert.deepEqual(childEnv(defaultAccount(), { HOME: "/h" }), {}, "the default account keeps the CLI's own login");
const named = { id: "work", configDir: "/tmp/work", env: {} };
assert.deepEqual(
childEnv(named, { HOME: "/h" }),
{ CLAUDE_CONFIG_DIR: "/tmp/work" },
"a named account gets its own directory",
);
// The Settings folder ~/.claude is the CLI's own: exporting it would sign the account out.
assert.deepEqual(childEnv({ ...defaultAccount(), configDir: "/h/.claude/" }, { HOME: "/h" }), {});
assert.deepEqual(childEnv({ ...defaultAccount(), configDir: "/h/.claude-work" }, { HOME: "/h" }), {
CLAUDE_CONFIG_DIR: "/h/.claude-work",
});
});
test("arguments are split like a shell", () => {
assert.deepEqual(splitArgs("--chrome --agent reviewer"), ["--chrome", "--agent", "reviewer"]);
assert.deepEqual(splitArgs('--append-system-prompt "be brief"'), ["--append-system-prompt", "be brief"]);
assert.deepEqual(splitArgs("'a b' c\\ d"), ["a b", "c d"]);
// An empty quoted word is a word, or a flag that takes an empty value
// would lose its argument.
assert.deepEqual(splitArgs('--name "" --x'), ["--name", "", "--x"]);
assert.deepEqual(splitArgs(" "), []);
});
test("launch arguments repeat a list flag for every value", () => {
const launch = {
mcpConfig: ["/w/mcp.json", "/w/other.json"],
strictMcpConfig: true,
settingSources: "user,project",
appendSystemPrompt: "be brief",
addDirs: ["/w/shared"],
extraArgs: ["--chrome"],
};
assert.deepEqual(launchArgs(launch, "/w/project"), [
"--mcp-config",
"/w/mcp.json",
"--mcp-config",
"/w/other.json",
"--strict-mcp-config",
"--setting-sources",
"user,project",
"--add-dir",
"/w/project",
"--add-dir",
"/w/shared",
"--chrome",
]);
assert.deepEqual(
launchFromEnv({
CLAUDE_MCP_CONFIG: "/a.json '/b c.json'",
CLAUDE_STRICT_MCP_CONFIG: "yes",
CLAUDE_ADD_DIRS: "/x",
CLAUDE_EXTRA_ARGS: "--chrome",
}),
{
mcpConfig: ["/a.json", "/b c.json"],
strictMcpConfig: true,
settingSources: null,
appendSystemPrompt: null,
addDirs: ["/x"],
extraArgs: ["--chrome"],
},
);
});
test("workflow switches default to on", () => {
assert.deepEqual(workflowSettings({}), { enabled: true, keyword: true });
assert.deepEqual(workflowSettings({ workflowKeywordTriggerEnabled: false }), { enabled: true, keyword: false });
assert.deepEqual(workflowSettings({ enableWorkflows: false }), { enabled: false, keyword: false });
assert.deepEqual(workflowSettings(null), { enabled: true, keyword: true });
});
test("an unconfigured launch only grants the workspace", () => {
assert.deepEqual(launchArgs(emptyLaunch(), "/w"), ["--add-dir", "/w"]);
});
test("the default account is the plain agent id", () => {
assert.equal(agentId(defaultAccount()), "claude");
assert.equal(displayName(defaultAccount()), "Claude Code");
});
test("a named instance gets its own agent id and directory", () => {
const [, instance] = parseInstances([{ id: "personal", configDir: "~/.claude_personal" }]);
assert.equal(agentId(instance), "claude:personal");
assert.equal(displayName(instance), "Claude Code (personal)");
assert.equal(configDirOf(instance, { HOME: "/Users/me" }), "/Users/me/.claude_personal", "`~` is expanded");
assert.equal(
parseInstances([{ id: "" }, { id: "a" }, { id: "a" }]).length,
2,
"missing and repeated ids are left out",
);
assert.equal(parseInstances("nonsense").length, 1);
});
/// Moving `HOME` would move the macOS keychain with it, and the CLI would
/// lose the login the user just made.
test("environment that would move the login is refused", () => {
const [, router] = parseInstances([
{
id: "router",
env: { HOME: "/tmp/elsewhere", ANTHROPIC_BASE_URL: "https://openrouter.ai/api", CLAUDE_CONFIG_DIR: "/x" },
},
]);
assert.deepEqual(childEnv(router, { HOME: "/h" }), { ANTHROPIC_BASE_URL: "https://openrouter.ai/api" });
});Versions
| Version | Published | Plugin API | Size | Permissions | Status |
|---|---|---|---|---|---|
| 0.2.0latest | Oct 5, 2026 | >=2 <3 | 128.7 KB | 4 permissions | Listed |
No comments yet.