feat(cli-tools): allow setting Claude Code max context tokens

Add a context-window selector on ClaudeToolCard that writes
CLAUDE_CODE_MAX_CONTEXT_TOKENS into settings.json (nudged 2K under the
labeled cap), and clear it on reset/default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
decolua
2026-07-29 18:10:31 +07:00
parent 6d96e24bd9
commit 8b0fcf4b16
2 changed files with 45 additions and 2 deletions

View File

@@ -9,6 +9,16 @@ import { matchKnownEndpoint } from "./cliEndpointMatch";
const CLOUD_URL = process.env.NEXT_PUBLIC_CLOUD_URL;
// Context window presets. UI shows the round number; the value written is nudged
// down 2K to stay safely under the upstream hard cap.
const CONTEXT_OPTIONS = [
{ label: "Default", value: "" },
{ label: "200K", value: "198000" },
{ label: "300K", value: "298000" },
{ label: "500K", value: "498000" },
{ label: "1M", value: "998000" },
];
export default function ClaudeToolCard({
tool,
isExpanded,
@@ -40,6 +50,7 @@ export default function ClaudeToolCard({
const [customBaseUrl, setCustomBaseUrl] = useState("");
const [ccFilterNaming, setCcFilterNaming] = useState(false);
const [exaMcpEnabled, setExaMcpEnabled] = useState(false);
const [maxContextTokens, setMaxContextTokens] = useState("");
const hasInitializedModels = useRef(false);
const getConfigStatus = () => {
@@ -65,6 +76,11 @@ export default function ClaudeToolCard({
}
}, [initialStatus]);
useEffect(() => {
const v = claudeStatus?.settings?.env?.CLAUDE_CODE_MAX_CONTEXT_TOKENS;
setMaxContextTokens(v || "");
}, [claudeStatus?.settings?.env?.CLAUDE_CODE_MAX_CONTEXT_TOKENS]);
useEffect(() => {
if (isExpanded) {
if (!claudeStatus) checkClaudeStatus();
@@ -163,10 +179,13 @@ export default function ClaudeToolCard({
const targetModel = modelMappings[model.alias];
if (targetModel && model.envKey) env[model.envKey] = targetModel;
});
if (maxContextTokens) {
env.CLAUDE_CODE_MAX_CONTEXT_TOKENS = maxContextTokens;
}
const res = await fetch("/api/cli-tools/claude-settings", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ env, exaMcpEnabled }),
body: JSON.stringify({ env, exaMcpEnabled, maxContextTokens }),
});
const data = await res.json();
if (res.ok) {
@@ -193,6 +212,7 @@ export default function ClaudeToolCard({
tool.defaultModels.forEach((model) => onModelMappingChange(model.alias, model.defaultValue || ""));
setSelectedApiKey("");
setExaMcpEnabled(false);
setMaxContextTokens("");
} else {
setMessage({ type: "error", text: data.error || "Failed to reset settings" });
}
@@ -222,6 +242,9 @@ export default function ClaudeToolCard({
const targetModel = modelMappings[model.alias];
if (targetModel && model.envKey) env[model.envKey] = targetModel;
});
if (maxContextTokens) {
env.CLAUDE_CODE_MAX_CONTEXT_TOKENS = maxContextTokens;
}
return [
{
@@ -345,6 +368,17 @@ export default function ClaudeToolCard({
</div>
))}
{/* Context Window */}
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-[8rem_auto_1fr_auto] sm:items-center sm:gap-2">
<span className="text-xs font-semibold text-text-main sm:text-right sm:text-sm">Context window</span>
<span className="material-symbols-outlined hidden text-text-muted text-[14px] sm:inline">arrow_forward</span>
<select value={maxContextTokens} onChange={(e) => setMaxContextTokens(e.target.value)} className="w-full min-w-0 px-2 py-2 bg-surface rounded border border-border text-xs focus:outline-none focus:ring-1 focus:ring-primary/50 sm:py-1.5">
{CONTEXT_OPTIONS.map((opt) => (
<option key={opt.label} value={opt.value}>{opt.label}</option>
))}
</select>
</div>
{/* CC Filter Naming */}
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-[8rem_auto_1fr_auto] sm:items-center sm:gap-2">
<span className="text-xs font-semibold text-text-main sm:text-right sm:text-sm">Filter naming</span>

View File

@@ -123,7 +123,7 @@ export async function GET() {
// POST - Backup old fields and write new settings
export async function POST(request) {
try {
const { env, exaMcpEnabled } = await request.json();
const { env, exaMcpEnabled, maxContextTokens } = await request.json();
if (!env || typeof env !== "object") {
return NextResponse.json(
@@ -166,6 +166,14 @@ export async function POST(request) {
},
};
// CLAUDE_CODE_MAX_CONTEXT_TOKENS — only set when a concrete value is chosen;
// "Default" removes the key so Claude Code falls back to the model's window.
if (maxContextTokens) {
newSettings.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS = String(maxContextTokens);
} else {
delete newSettings.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS;
}
// Write new settings
await fs.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
@@ -195,6 +203,7 @@ const RESET_ENV_KEYS = [
"ANTHROPIC_DEFAULT_SONNET_MODEL",
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
"API_TIMEOUT_MS",
"CLAUDE_CODE_MAX_CONTEXT_TOKENS",
];
// DELETE - Reset settings (remove env fields)