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>
84 lines
2.9 KiB
TypeScript
84 lines
2.9 KiB
TypeScript
/**
|
|
* Thinking effort configuration for mapping pi's ThinkingLevel to Claude CLI --effort flags.
|
|
*
|
|
* Maps pi's reasoning levels (minimal/low/medium/high/xhigh) to the CLI's effort
|
|
* levels (low/medium/high/max). Opus models get an elevated mapping where medium
|
|
* becomes high and high becomes max, leveraging their superior reasoning capability.
|
|
*
|
|
* IMPORTANT: The CLI does NOT support --thinking-budget. Only --effort is supported.
|
|
*/
|
|
|
|
import type { ThinkingLevel, ThinkingBudgets } from "@mariozechner/pi-ai";
|
|
|
|
/** CLI effort levels accepted by the --effort flag */
|
|
export type CliEffortLevel = "low" | "medium" | "high" | "max";
|
|
|
|
/**
|
|
* Standard model mapping: pi ThinkingLevel -> CLI effort.
|
|
* Non-Opus models never receive "max" (would cause CLI error).
|
|
*/
|
|
const STANDARD_EFFORT_MAP: Record<ThinkingLevel, CliEffortLevel> = {
|
|
minimal: "low",
|
|
low: "low",
|
|
medium: "medium",
|
|
high: "high",
|
|
xhigh: "high", // non-Opus: silently downgrade (max not supported)
|
|
};
|
|
|
|
/**
|
|
* Opus model mapping: shifted up for elevated reasoning.
|
|
* Opus models get max capability at high/xhigh levels.
|
|
*/
|
|
const OPUS_EFFORT_MAP: Record<ThinkingLevel, CliEffortLevel> = {
|
|
minimal: "low",
|
|
low: "low",
|
|
medium: "high", // shifted: standard high
|
|
high: "max", // shifted: maximum capability
|
|
xhigh: "max", // Opus gets max
|
|
};
|
|
|
|
/**
|
|
* Detect whether a model ID refers to an Opus model.
|
|
* Uses includes('opus') for forward-compatibility with future Opus versions.
|
|
*
|
|
* @param modelId - The model identifier string
|
|
* @returns true if the model is an Opus variant
|
|
*/
|
|
export function isOpusModel(modelId: string): boolean {
|
|
return modelId.includes("opus");
|
|
}
|
|
|
|
/**
|
|
* Map pi's ThinkingLevel to a CLI effort string.
|
|
*
|
|
* When reasoning is undefined, returns undefined so the --effort flag is omitted
|
|
* entirely, letting the CLI use its default behavior. When thinkingBudgets are
|
|
* provided, a console.warn is logged because the CLI only supports effort levels,
|
|
* not token budgets.
|
|
*
|
|
* @param reasoning - Pi's thinking level (undefined = omit flag)
|
|
* @param modelId - Model ID for Opus detection
|
|
* @param thinkingBudgets - Custom budgets (logged as unsupported, not applied)
|
|
* @returns CLI effort level string, or undefined if flag should be omitted
|
|
*/
|
|
export function mapThinkingEffort(
|
|
reasoning?: ThinkingLevel,
|
|
modelId?: string,
|
|
thinkingBudgets?: ThinkingBudgets,
|
|
): CliEffortLevel | undefined {
|
|
if (reasoning === undefined) {
|
|
return undefined; // omit --effort flag entirely
|
|
}
|
|
|
|
if (thinkingBudgets && Object.keys(thinkingBudgets).length > 0) {
|
|
console.warn(
|
|
"[pi-claude-cli] Custom thinkingBudgets are not supported with CLI subprocess. " +
|
|
"The CLI uses --effort levels instead of token budgets. Budgets will be ignored.",
|
|
);
|
|
}
|
|
|
|
const isOpus = modelId ? isOpusModel(modelId) : false;
|
|
const map = isOpus ? OPUS_EFFORT_MAP : STANDARD_EFFORT_MAP;
|
|
return map[reasoning];
|
|
}
|