Replaces the stray useClaudeCli settings checkbox + onboarding question
with a proper provider-card UX. The card lives next to OAuth + API-key
cards in onboarding and settings, with Enable/Disable + Test actions.
Backend:
- Vendors rchern/pi-claude-cli@0.3.1 as packages/pi-claude-cli
(MIT, attribution in UPSTREAM.md). Lets us bump peer-dep on
pi-coding-agent in lockstep with Fusion (upstream pinned ^0.52.0
vs ours ^0.62.0) and fix bugs without waiting on upstream.
- Adds @fusion/pi-claude-cli as a workspace dep of @runfusion/fusion
so users don't have to `npm install -g pi-claude-cli` manually.
- serve/daemon/dashboard conditionally load the extension via
discoverAndLoadExtensions() when GlobalSettings.useClaudeCli is on;
no side-effects on user ~/.fusion/agent/settings.json.
- New GET /api/providers/claude-cli/status: claude --version probe
+ toggle state + cached extension resolution.
- New POST /api/auth/claude-cli: flips useClaudeCli, refuses if the
claude binary is missing, fires the existing skill-backfill hook.
- /api/auth/status now injects a synthetic {id:"claude-cli", type:"cli"}
provider entry so onboarding + settings see a consistent list.
Frontend:
- New ClaudeCliProviderCard component shared between ModelOnboardingModal
and SettingsModal's Authentication section.
- New AuthProvider.type = "cli" variant.
- Removed the old "Route AI calls through the Claude CLI" checkbox from
Global Models settings and the opt-in step from the onboarding wizard.
- ProviderIcon gets a composite Anthropic-mark-plus-terminal glyph for
the claude-cli provider id.
Tests:
- 8 unit tests for extension resolution (@fusion/pi-claude-cli is
workspace-linked so these run in-tree).
- 2 unit tests for the binary probe.
- Existing /auth/status tests filter out the new synthetic entry so
they keep asserting structural OAuth/API-key behavior in isolation.
- The vendored package's own 296 tests still pass unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
1.9 KiB
TypeScript
69 lines
1.9 KiB
TypeScript
/**
|
|
* Control protocol handler for Claude CLI stream-json communication.
|
|
*
|
|
* Processes control_request messages from Claude CLI stdout and writes
|
|
* control_response messages to stdin.
|
|
*
|
|
* - Custom MCP tools (mcp__custom-tools__*): DENIED — pi executes these
|
|
* - Everything else (user MCP tools, internal tools): ALLOWED — Claude handles
|
|
*/
|
|
|
|
import type { ClaudeControlRequest } from "./types";
|
|
import { CUSTOM_TOOLS_MCP_PREFIX } from "./tool-mapping.js";
|
|
|
|
export const TOOL_EXECUTION_DENIED_MESSAGE =
|
|
"Tool execution is unavailable in this environment.";
|
|
|
|
/** Prefix for MCP (Model Context Protocol) tool names. */
|
|
export const MCP_PREFIX = "mcp__";
|
|
|
|
interface ControlResponse {
|
|
type: "control_response";
|
|
request_id: string;
|
|
response: {
|
|
subtype: "success";
|
|
response: {
|
|
behavior: "allow" | "deny";
|
|
message?: string;
|
|
};
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Handle a control_request from the Claude CLI.
|
|
*
|
|
* Denies custom MCP tools (mcp__custom-tools__*) so pi can execute them.
|
|
* Allows everything else (user MCP tools, internal Claude tools).
|
|
*
|
|
* @returns true if the tool was allowed, false if denied
|
|
*/
|
|
export function handleControlRequest(
|
|
msg: ClaudeControlRequest,
|
|
stdin: NodeJS.WritableStream,
|
|
): boolean {
|
|
if (!msg.request_id || !msg.request) {
|
|
console.error(
|
|
"[pi-claude-cli] Malformed control_request: missing request_id or request object",
|
|
msg,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
const toolName = msg.request?.tool_name ?? "";
|
|
const isCustomTool = toolName.startsWith(CUSTOM_TOOLS_MCP_PREFIX);
|
|
|
|
const response: ControlResponse = {
|
|
type: "control_response",
|
|
request_id: msg.request_id,
|
|
response: {
|
|
subtype: "success",
|
|
response: isCustomTool
|
|
? { behavior: "deny", message: TOOL_EXECUTION_DENIED_MESSAGE }
|
|
: { behavior: "allow" },
|
|
},
|
|
};
|
|
|
|
stdin.write(JSON.stringify(response) + "\n");
|
|
return !isCustomTool;
|
|
}
|