feat: build

This commit is contained in:
claude-code-best committed 2026-03-31 19:22:47 +08:00
commit f90eee85d8
1906 files changed
+514339

No files matched your search

+290
View File
@@ -0,0 +1,290 @@
import { c as _c } from "react/compiler-runtime";
import { homedir } from 'os';
import React from 'react';
import { logEvent } from 'src/services/analytics/index.js';
import { setSessionTrustAccepted } from '../../bootstrap/state.js';
import type { Command } from '../../commands.js';
import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js';
import { Box, Link, Text } from '../../ink.js';
import { useKeybinding } from '../../keybindings/useKeybinding.js';
import { getMcpConfigsByScope } from '../../services/mcp/config.js';
import { BASH_TOOL_NAME } from '../../tools/BashTool/toolName.js';
import { checkHasTrustDialogAccepted, saveCurrentProjectConfig } from '../../utils/config.js';
import { getCwd } from '../../utils/cwd.js';
import { getFsImplementation } from '../../utils/fsOperations.js';
import { gracefulShutdownSync } from '../../utils/gracefulShutdown.js';
import { Select } from '../CustomSelect/index.js';
import { PermissionDialog } from '../permissions/PermissionDialog.js';
import { getApiKeyHelperSources, getAwsCommandsSources, getBashPermissionSources, getDangerousEnvVarsSources, getGcpCommandsSources, getHooksSources, getOtelHeadersHelperSources } from './utils.js';
type Props = {
onDone(): void;
commands?: Command[];
};
export function TrustDialog(t0) {
const $ = _c(33);
const {
onDone,
commands
} = t0;
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t1 = getMcpConfigsByScope("project");
$[0] = t1;
} else {
t1 = $[0];
}
const {
servers: projectServers
} = t1;
let t2;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
t2 = Object.keys(projectServers);
$[1] = t2;
} else {
t2 = $[1];
}
const hasMcpServers = t2.length > 0;
let t3;
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
t3 = getHooksSources();
$[2] = t3;
} else {
t3 = $[2];
}
const hooksSettingSources = t3;
const hasHooks = hooksSettingSources.length > 0;
let t4;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
t4 = getBashPermissionSources();
$[3] = t4;
} else {
t4 = $[3];
}
const bashSettingSources = t4;
let t5;
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
t5 = getApiKeyHelperSources();
$[4] = t5;
} else {
t5 = $[4];
}
const apiKeyHelperSources = t5;
const hasApiKeyHelper = apiKeyHelperSources.length > 0;
let t6;
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
t6 = getAwsCommandsSources();
$[5] = t6;
} else {
t6 = $[5];
}
const awsCommandsSources = t6;
const hasAwsCommands = awsCommandsSources.length > 0;
let t7;
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
t7 = getGcpCommandsSources();
$[6] = t7;
} else {
t7 = $[6];
}
const gcpCommandsSources = t7;
const hasGcpCommands = gcpCommandsSources.length > 0;
let t8;
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
t8 = getOtelHeadersHelperSources();
$[7] = t8;
} else {
t8 = $[7];
}
const otelHeadersHelperSources = t8;
const hasOtelHeadersHelper = otelHeadersHelperSources.length > 0;
let t9;
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
t9 = getDangerousEnvVarsSources();
$[8] = t9;
} else {
t9 = $[8];
}
const dangerousEnvVarsSources = t9;
const hasDangerousEnvVars = dangerousEnvVarsSources.length > 0;
let t10;
if ($[9] !== commands) {
t10 = commands?.some(_temp2) ?? false;
$[9] = commands;
$[10] = t10;
} else {
t10 = $[10];
}
const hasSlashCommandBash = t10;
let t11;
if ($[11] !== commands) {
t11 = commands?.some(_temp4) ?? false;
$[11] = commands;
$[12] = t11;
} else {
t11 = $[12];
}
const hasSkillsBash = t11;
const hasAnyBashExecution = bashSettingSources.length > 0 || hasSlashCommandBash || hasSkillsBash;
const hasTrustDialogAccepted = checkHasTrustDialogAccepted();
let t12;
let t13;
if ($[13] !== hasAnyBashExecution) {
t12 = () => {
const isHomeDir = homedir() === getCwd();
logEvent("tengu_trust_dialog_shown", {
isHomeDir,
hasMcpServers,
hasHooks,
hasBashExecution: hasAnyBashExecution,
hasApiKeyHelper,
hasAwsCommands,
hasGcpCommands,
hasOtelHeadersHelper,
hasDangerousEnvVars
});
};
t13 = [hasMcpServers, hasHooks, hasAnyBashExecution, hasApiKeyHelper, hasAwsCommands, hasGcpCommands, hasOtelHeadersHelper, hasDangerousEnvVars];
$[13] = hasAnyBashExecution;
$[14] = t12;
$[15] = t13;
} else {
t12 = $[14];
t13 = $[15];
}
React.useEffect(t12, t13);
let t14;
if ($[16] !== hasAnyBashExecution || $[17] !== onDone) {
t14 = function onChange(value) {
if (value === "exit") {
gracefulShutdownSync(1);
return;
}
const isHomeDir_0 = homedir() === getCwd();
logEvent("tengu_trust_dialog_accept", {
isHomeDir: isHomeDir_0,
hasMcpServers,
hasHooks,
hasBashExecution: hasAnyBashExecution,
hasApiKeyHelper,
hasAwsCommands,
hasGcpCommands,
hasOtelHeadersHelper,
hasDangerousEnvVars
});
if (isHomeDir_0) {
setSessionTrustAccepted(true);
} else {
saveCurrentProjectConfig(_temp5);
}
onDone();
};
$[16] = hasAnyBashExecution;
$[17] = onDone;
$[18] = t14;
} else {
t14 = $[18];
}
const onChange = t14;
const exitState = useExitOnCtrlCDWithKeybindings(_temp6);
let t15;
if ($[19] === Symbol.for("react.memo_cache_sentinel")) {
t15 = {
context: "Confirmation"
};
$[19] = t15;
} else {
t15 = $[19];
}
useKeybinding("confirm:no", _temp7, t15);
if (hasTrustDialogAccepted) {
setTimeout(onDone);
return null;
}
let t16;
let t17;
let t18;
if ($[20] === Symbol.for("react.memo_cache_sentinel")) {
t16 = <Text bold={true}>{getFsImplementation().cwd()}</Text>;
t17 = <Text>Quick safety check: Is this a project you created or one you trust? (Like your own code, a well-known open source project, or work from your team). If not, take a moment to review what{"'"}s in this folder first.</Text>;
t18 = <Text>Claude Code{"'"}ll be able to read, edit, and execute files here.</Text>;
$[20] = t16;
$[21] = t17;
$[22] = t18;
} else {
t16 = $[20];
t17 = $[21];
t18 = $[22];
}
let t19;
if ($[23] === Symbol.for("react.memo_cache_sentinel")) {
t19 = <Text dimColor={true}><Link url="https://code.claude.com/docs/en/security">Security guide</Link></Text>;
$[23] = t19;
} else {
t19 = $[23];
}
let t20;
if ($[24] === Symbol.for("react.memo_cache_sentinel")) {
t20 = [{
label: "Yes, I trust this folder",
value: "enable_all"
}, {
label: "No, exit",
value: "exit"
}];
$[24] = t20;
} else {
t20 = $[24];
}
let t21;
if ($[25] !== onChange) {
t21 = <Select options={t20} onChange={value_0 => onChange(value_0 as 'enable_all' | 'exit')} onCancel={() => onChange("exit")} />;
$[25] = onChange;
$[26] = t21;
} else {
t21 = $[26];
}
let t22;
if ($[27] !== exitState.keyName || $[28] !== exitState.pending) {
t22 = <Text dimColor={true}>{exitState.pending ? <>Press {exitState.keyName} again to exit</> : <>Enter to confirm · Esc to cancel</>}</Text>;
$[27] = exitState.keyName;
$[28] = exitState.pending;
$[29] = t22;
} else {
t22 = $[29];
}
let t23;
if ($[30] !== t21 || $[31] !== t22) {
t23 = <PermissionDialog color="warning" titleColor="warning" title="Accessing workspace:"><Box flexDirection="column" gap={1} paddingTop={1}>{t16}{t17}{t18}{t19}{t21}{t22}</Box></PermissionDialog>;
$[30] = t21;
$[31] = t22;
$[32] = t23;
} else {
t23 = $[32];
}
return t23;
}
function _temp7() {
gracefulShutdownSync(0);
}
function _temp6() {
return gracefulShutdownSync(1);
}
function _temp5(current) {
return {
...current,
hasTrustDialogAccepted: true
};
}
function _temp4(command_0) {
return command_0.type === "prompt" && (command_0.loadedFrom === "skills" || command_0.loadedFrom === "plugin") && (command_0.source === "projectSettings" || command_0.source === "localSettings" || command_0.source === "plugin") && command_0.allowedTools?.some(_temp3);
}
function _temp3(tool_0) {
return tool_0 === BASH_TOOL_NAME || tool_0.startsWith(BASH_TOOL_NAME + "(");
}
function _temp2(command) {
return command.type === "prompt" && command.loadedFrom === "commands_DEPRECATED" && (command.source === "projectSettings" || command.source === "localSettings") && command.allowedTools?.some(_temp);
}
function _temp(tool) {
return tool === BASH_TOOL_NAME || tool.startsWith(BASH_TOOL_NAME + "(");
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJob21lZGlyIiwiUmVhY3QiLCJsb2dFdmVudCIsInNldFNlc3Npb25UcnVzdEFjY2VwdGVkIiwiQ29tbWFuZCIsInVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncyIsIkJveCIsIkxpbmsiLCJUZXh0IiwidXNlS2V5YmluZGluZyIsImdldE1jcENvbmZpZ3NCeVNjb3BlIiwiQkFTSF9UT09MX05BTUUiLCJjaGVja0hhc1RydXN0RGlhbG9nQWNjZXB0ZWQiLCJzYXZlQ3VycmVudFByb2plY3RDb25maWciLCJnZXRDd2QiLCJnZXRGc0ltcGxlbWVudGF0aW9uIiwiZ3JhY2VmdWxTaHV0ZG93blN5bmMiLCJTZWxlY3QiLCJQZXJtaXNzaW9uRGlhbG9nIiwiZ2V0QXBpS2V5SGVscGVyU291cmNlcyIsImdldEF3c0NvbW1hbmRzU291cmNlcyIsImdldEJhc2hQZXJtaXNzaW9uU291cmNlcyIsImdldERhbmdlcm91c0VudlZhcnNTb3VyY2VzIiwiZ2V0R2NwQ29tbWFuZHNTb3VyY2VzIiwiZ2V0SG9va3NTb3VyY2VzIiwiZ2V0T3RlbEhlYWRlcnNIZWxwZXJTb3VyY2VzIiwiUHJvcHMiLCJvbkRvbmUiLCJjb21tYW5kcyIsIlRydXN0RGlhbG9nIiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN5bWJvbCIsImZvciIsInNlcnZlcnMiLCJwcm9qZWN0U2VydmVycyIsInQyIiwiT2JqZWN0Iiwia2V5cyIsImhhc01jcFNlcnZlcnMiLCJsZW5ndGgiLCJ0MyIsImhvb2tzU2V0dGluZ1NvdXJjZXMiLCJoYXNIb29rcyIsInQ0IiwiYmFzaFNldHRpbmdTb3VyY2VzIiwidDUiLCJhcGlLZXlIZWxwZXJTb3VyY2VzIiwiaGFzQXBpS2V5SGVscGVyIiwidDYiLCJhd3NDb21tYW5kc1NvdXJjZXMiLCJoYXNBd3NDb21tYW5kcyIsInQ3IiwiZ2NwQ29tbWFuZHNTb3VyY2VzIiwiaGFzR2NwQ29tbWFuZHMiLCJ0OCIsIm90ZWxIZWFkZXJzSGVscGVyU291cmNlcyIsImhhc090ZWxIZWFkZXJzSGVscGVyIiwidDkiLCJkYW5nZXJvdXNFbnZWYXJzU291cmNlcyIsImhhc0Rhbmdlcm91c0VudlZhcnMiLCJ0MTAiLCJzb21lIiwiX3RlbXAyIiwiaGFzU2xhc2hDb21tYW5kQmFzaCIsInQxMSIsIl90ZW1wNCIsImhhc1NraWxsc0Jhc2giLCJoYXNBbnlCYXNoRXhlY3V0aW9uIiwiaGFzVHJ1c3REaWFsb2dBY2NlcHRlZCIsInQxMiIsInQxMyIsImlzSG9tZURpciIsImhhc0Jhc2hFeGVjdXRpb24iLCJ1c2VFZmZlY3QiLCJ0MTQiLCJvbkNoYW5nZSIsInZhbHVlIiwiaXNIb21lRGlyXzAiLCJfdGVtcDUiLCJleGl0U3RhdGUiLCJfdGVtcDYiLCJ0MTUiLCJjb250ZXh0IiwiX3RlbXA3Iiwic2V0VGltZW91dCIsInQxNiIsInQxNyIsInQxOCIsImN3ZCIsInQxOSIsInQyMCIsImxhYmVsIiwidDIxIiwidmFsdWVfMCIsInQyMiIsImtleU5hbWUiLCJwZW5kaW5nIiwidDIzIiwiY3VycmVudCIsImNvbW1hbmRfMCIsImNvbW1hbmQiLCJ0eXBlIiwibG9hZGVkRnJvbSIsInNvdXJjZSIsImFsbG93ZWRUb29scyIsIl90ZW1wMyIsInRvb2xfMCIsInRvb2wiLCJzdGFydHNXaXRoIiwiX3RlbXAiXSwic291cmNlcyI6WyJUcnVzdERpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgaG9tZWRpciB9IGZyb20gJ29zJ1xuaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgbG9nRXZlbnQgfSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgc2V0U2Vzc2lvblRydXN0QWNjZXB0ZWQgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IENvbW1hbmQgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IHVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncyB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncy5qcydcbmltcG9ydCB7IEJveCwgTGluaywgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgZ2V0TWNwQ29uZmlnc0J5U2NvcGUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvY29uZmlnLmpzJ1xuaW1wb3J0IHsgQkFTSF9UT09MX05BTUUgfSBmcm9tICcuLi8uLi90b29scy9CYXNoVG9vbC90b29sTmFtZS5qcydcbmltcG9ydCB7XG4gIGNoZWNrSGFzVHJ1c3REaWFsb2dBY2NlcHRlZCxcbiAgc2F2ZUN1cnJlbnRQcm9qZWN0Q29uZmlnLFxufSBmcm9tICcuLi8uLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgeyBnZXRDd2QgfSBmcm9tICcuLi8uLi91dGlscy9jd2QuanMnXG5pbXBvcnQgeyBnZXRGc0ltcGxlbWVudGF0aW9uIH0gZnJvbSAnLi4vLi4vdXRpbHMvZnNPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHsgZ3JhY2VmdWxTaHV0ZG93blN5bmMgfSBmcm9tICcuLi8uLi91dGlscy9ncmFjZWZ1bFNodXRkb3duLmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi4vQ3VzdG9tU2VsZWN0L2luZGV4LmpzJ1xuaW1wb3J0IHsgUGVybWlzc2lvbkRpYWxvZyB9IGZyb20gJy4uL3Blcm1pc3Npb25zL1Blcm1pc3Npb25EaWFsb2cuanMnXG5pbXBvcnQge1xuICBnZXRBcGlLZXlIZWxwZXJTb3VyY2VzLFxuICBnZXRBd3NDb21tYW5kc1NvdXJjZXMsXG4gIGdldEJhc2hQZXJtaXNzaW9uU291cmNlcyxcbiAgZ2V0RGFuZ2Vyb3VzRW52VmFyc1NvdXJjZXMsXG4gIGdldEdjcENvbW1hbmRzU291cmNlcyxcbiAgZ2V0SG9va3NTb3VyY2VzLFxuICBnZXRPdGVsSGVhZGVyc0hlbHBlclNvdXJjZXMsXG59IGZyb20gJy4vdXRpbHMuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG9uRG9uZSgpOiB2b2lkXG4gIGNvbW1hbmRzPzogQ29tbWFuZFtdXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBUcnVzdERpYWxvZyh7IG9uRG9uZSwgY29tbWFuZHMgfTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCB7IHNlcnZlcnM6IHByb2plY3RTZXJ2ZXJzIH0gPSBnZXRNY3BDb25maWdzQnlTY29wZSgncHJvamVjdCcpXG5cbiAgLy8gSW4gYWxsIGNhc2VzLCB3ZSBnZW5lcmFsbHkgY2hlY2sgb25seSB0aGUgcHJvamVjdC1sZXZlbCBhbmRcbiAgLy8gcHJvamVjdC1sb2NhbC1sZXZlbCBzZXR0aW5ncywgd2hpY2ggd2UgYXNzdW1lIHRoYXQgdXNlcnMgZG8gbm90IGNvbmZpZ3VyZVxuICAvLyBkaXJlY3RseSBjb21wYXJlZCB0byB1c2VyLWxldmVsIHNldHRpbmdzLlxuXG4gIC8vIENoZWNrIGZvciBNQ1BzXG4gIGNvbnN0IGhhc01jcFNlcnZlcnMgPSBPYmplY3Qua2V5cyhwcm9qZWN0U2VydmVycykubGVuZ3RoID4gMFxuICAvLyBDaGVjayBmb3IgaG9va3NcbiAgY29uc3QgaG9va3NTZXR0aW5nU291cmNlcyA9IGdldEhvb2tzU291cmNlcygpXG4gIGNvbnN0IGhhc0hvb2tzID0gaG9va3NTZXR0aW5nU291cmNlcy5sZW5ndGggPiAwXG4gIC8vIENoZWNrIHdoZXRoZXIgY29kZSBleGVjdXRpb24gaXMgYWxsb3dlZCBpbiBwZXJtaXNzaW9ucyBhbmQgc2xhc2ggY29tbWFuZHNcbiAgY29uc3QgYmFzaFNldHRpbmdTb3VyY2VzID0gZ2V0QmFzaFBlcm1pc3Npb25Tb3VyY2VzKClcbiAgLy8gQ2hlY2sgZm9yIGFwaUtleUhlbHBlciB3aGljaCBleGVjdXRlcyBhcmJpdHJhcnkgY29tbWFuZHNcbiAgY29uc3QgYXBpS2V5SGVscGVyU291cmNlcyA9IGdldEFwaUtleUhlbHBlclNvdXJjZXMoKVxuICBjb25zdCBoYXNBcGlLZXlIZWxwZXIgPSBhcGlLZXlIZWxwZXJTb3VyY2VzLmxlbmd0aCA+IDBcbiAgLy8gQ2hlY2sgZm9yIEFXUyBjb21tYW5kcyB3aGljaCBleGVjdXRlIGF Line truncated
+245
View File
@@ -0,0 +1,245 @@
import type { PermissionRule } from 'src/utils/permissions/PermissionRule.js'
import { getSettingsForSource } from 'src/utils/settings/settings.js'
import type { SettingsJson } from 'src/utils/settings/types.js'
import { BASH_TOOL_NAME } from '../../tools/BashTool/toolName.js'
import { SAFE_ENV_VARS } from '../../utils/managedEnvConstants.js'
import { getPermissionRulesForSource } from '../../utils/permissions/permissionsLoader.js'
function hasHooks(settings: SettingsJson | null): boolean {
if (settings === null || settings.disableAllHooks) {
return false
}
if (settings.statusLine) {
return true
}
if (settings.fileSuggestion) {
return true
}
if (!settings.hooks) {
return false
}
for (const hookConfig of Object.values(settings.hooks)) {
if (hookConfig.length > 0) {
return true
}
}
return false
}
export function getHooksSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasHooks(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasHooks(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
function hasBashPermission(rules: PermissionRule[]): boolean {
return rules.some(
rule =>
rule.ruleBehavior === 'allow' &&
(rule.ruleValue.toolName === BASH_TOOL_NAME ||
rule.ruleValue.toolName.startsWith(BASH_TOOL_NAME + '(')),
)
}
/**
* Get which setting sources have bash allow rules.
* Returns an array of file paths that have bash permissions.
*/
export function getBashPermissionSources(): string[] {
const sources: string[] = []
const projectRules = getPermissionRulesForSource('projectSettings')
if (hasBashPermission(projectRules)) {
sources.push('.claude/settings.json')
}
const localRules = getPermissionRulesForSource('localSettings')
if (hasBashPermission(localRules)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Format a list of items with proper "and" conjunction.
* @param items - Array of items to format
* @param limit - Optional limit for how many items to show before summarizing (ignored if 0)
*/
export function formatListWithAnd(items: string[], limit?: number): string {
if (items.length === 0) return ''
// Ignore limit if it's 0
const effectiveLimit = limit === 0 ? undefined : limit
// If no limit or items are within limit, use normal formatting
if (!effectiveLimit || items.length <= effectiveLimit) {
if (items.length === 1) return items[0]!
if (items.length === 2) return `${items[0]} and ${items[1]}`
const lastItem = items[items.length - 1]!
const allButLast = items.slice(0, -1)
return `${allButLast.join(', ')}, and ${lastItem}`
}
// If we have more items than the limit, show first few and count the rest
const shown = items.slice(0, effectiveLimit)
const remaining = items.length - effectiveLimit
if (shown.length === 1) {
return `${shown[0]} and ${remaining} more`
}
return `${shown.join(', ')}, and ${remaining} more`
}
/**
* Check if settings have otelHeadersHelper configured
*/
function hasOtelHeadersHelper(settings: SettingsJson | null): boolean {
return !!settings?.otelHeadersHelper
}
/**
* Get which setting sources have otelHeadersHelper configured.
* Returns an array of file paths that have otelHeadersHelper.
*/
export function getOtelHeadersHelperSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasOtelHeadersHelper(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasOtelHeadersHelper(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Check if settings have apiKeyHelper configured
*/
function hasApiKeyHelper(settings: SettingsJson | null): boolean {
return !!settings?.apiKeyHelper
}
/**
* Get which setting sources have apiKeyHelper configured.
* Returns an array of file paths that have apiKeyHelper.
*/
export function getApiKeyHelperSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasApiKeyHelper(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasApiKeyHelper(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Check if settings have AWS commands configured
*/
function hasAwsCommands(settings: SettingsJson | null): boolean {
return !!(settings?.awsAuthRefresh || settings?.awsCredentialExport)
}
/**
* Get which setting sources have AWS commands configured.
* Returns an array of file paths that have awsAuthRefresh or awsCredentialExport.
*/
export function getAwsCommandsSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasAwsCommands(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasAwsCommands(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Check if settings have GCP commands configured
*/
function hasGcpCommands(settings: SettingsJson | null): boolean {
return !!settings?.gcpAuthRefresh
}
/**
* Get which setting sources have GCP commands configured.
* Returns an array of file paths that have gcpAuthRefresh.
*/
export function getGcpCommandsSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasGcpCommands(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasGcpCommands(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Check if settings have dangerous environment variables configured.
* Any env var NOT in SAFE_ENV_VARS is considered dangerous.
*/
function hasDangerousEnvVars(settings: SettingsJson | null): boolean {
if (!settings?.env) {
return false
}
return Object.keys(settings.env).some(
key => !SAFE_ENV_VARS.has(key.toUpperCase()),
)
}
/**
* Get which setting sources have dangerous environment variables configured.
* Returns an array of file paths that have env vars not in SAFE_ENV_VARS.
*/
export function getDangerousEnvVarsSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasDangerousEnvVars(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasDangerousEnvVars(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}