更新大量 tsx 原始文件; 已经迁移 login panel; 部分 (#121)

* style(B1-1): 格式化 ink/buddy/cli/context/screens/tasks/services/keybindings/state (43 files)

纯格式化:移除分号、React Compiler import、import 多行展开。
修复了 Box.tsx 和 ScrollBox.tsx 中无效的 global.d.ts import。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-2): 格式化 commands (79 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-3): 格式化 components/messages,permissions,mcp,sandbox,shell (104 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-4): 格式化 components/PromptInput,FeedbackSurvey,tasks,agents,skills,design-system,wizard (73 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-5): 格式化 components其余 + hooks + tools (232 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(B1-6): 格式化 main/entrypoints/utils/moreright (21 files)

纯格式化:移除分号、React Compiler import、import 多行展开。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 更新 README,新增 Run.ps1/TODO.md,删除 V6.md

- README.md: 大幅重写,更详细版本历史和配置示例
- Run.ps1: 新增 Windows 启动脚本
- TODO.md: 新增包完成清单
- V6.md: 删除(架构重构规划已不适用)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复以前的问题

* fix: 修复 login 面板的问题

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-04 23:24:27 +08:00
committed by GitHub
parent 02694918b5
commit 5b1a52b8e0
559 changed files with 103807 additions and 101817 deletions

View File

@@ -1,4 +1,3 @@
import { c as _c } from "react/compiler-runtime";
/**
* HooksConfigMenu is a read-only browser for configured hooks.
*
@@ -11,567 +10,324 @@ import { c as _c } from "react/compiler-runtime";
* command-type hooks and duplicating the settings.json editing surface
* in-menu for all four types would be a maintenance burden.
*/
import * as React from 'react';
import { useCallback, useMemo, useState } from 'react';
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
import { useAppState, useAppStateStore } from 'src/state/AppState.js';
import type { CommandResultDisplay } from '../../commands.js';
import { useSettingsChange } from '../../hooks/useSettingsChange.js';
import { Box, Text } from '../../ink.js';
import { useKeybinding } from '../../keybindings/useKeybinding.js';
import { getHookEventMetadata, getHooksForMatcher, getMatcherMetadata, getSortedMatchersForEvent, groupHooksByEventAndMatcher } from '../../utils/hooks/hooksConfigManager.js';
import type { IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
import { getSettings_DEPRECATED, getSettingsForSource } from '../../utils/settings/settings.js';
import { plural } from '../../utils/stringUtils.js';
import { Dialog } from '../design-system/Dialog.js';
import { SelectEventMode } from './SelectEventMode.js';
import { SelectHookMode } from './SelectHookMode.js';
import { SelectMatcherMode } from './SelectMatcherMode.js';
import { ViewHookMode } from './ViewHookMode.js';
import * as React from 'react'
import { useCallback, useMemo, useState } from 'react'
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
import { useAppState, useAppStateStore } from 'src/state/AppState.js'
import type { CommandResultDisplay } from '../../commands.js'
import { useSettingsChange } from '../../hooks/useSettingsChange.js'
import { Box, Text } from '../../ink.js'
import { useKeybinding } from '../../keybindings/useKeybinding.js'
import {
getHookEventMetadata,
getHooksForMatcher,
getMatcherMetadata,
getSortedMatchersForEvent,
groupHooksByEventAndMatcher,
} from '../../utils/hooks/hooksConfigManager.js'
import type { IndividualHookConfig } from '../../utils/hooks/hooksSettings.js'
import {
getSettings_DEPRECATED,
getSettingsForSource,
} from '../../utils/settings/settings.js'
import { plural } from '../../utils/stringUtils.js'
import { Dialog } from '../design-system/Dialog.js'
import { SelectEventMode } from './SelectEventMode.js'
import { SelectHookMode } from './SelectHookMode.js'
import { SelectMatcherMode } from './SelectMatcherMode.js'
import { ViewHookMode } from './ViewHookMode.js'
type Props = {
toolNames: string[];
onExit: (result?: string, options?: {
display?: CommandResultDisplay;
}) => void;
};
type ModeState = {
mode: 'select-event';
} | {
mode: 'select-matcher';
event: HookEvent;
} | {
mode: 'select-hook';
event: HookEvent;
matcher: string;
} | {
mode: 'view-hook';
event: HookEvent;
hook: IndividualHookConfig;
};
export function HooksConfigMenu(t0) {
const $ = _c(100);
const {
toolNames,
onExit
} = t0;
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t1 = {
mode: "select-event"
};
$[0] = t1;
} else {
t1 = $[0];
}
const [modeState, setModeState] = useState(t1);
const [disabledByPolicy, setDisabledByPolicy] = useState(_temp);
const [restrictedByPolicy, setRestrictedByPolicy] = useState(_temp2);
let t2;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
t2 = source => {
if (source === "policySettings") {
const settings_0 = getSettings_DEPRECATED();
const hooksDisabled_0 = settings_0?.disableAllHooks === true;
setDisabledByPolicy(hooksDisabled_0 && getSettingsForSource("policySettings")?.disableAllHooks === true);
setRestrictedByPolicy(getSettingsForSource("policySettings")?.allowManagedHooksOnly === true);
}
};
$[1] = t2;
} else {
t2 = $[1];
}
useSettingsChange(t2);
const mode = modeState.mode;
const selectedEvent = "event" in modeState ? modeState.event : "PreToolUse";
const selectedMatcher = "matcher" in modeState ? modeState.matcher : null;
const mcp = useAppState(_temp3);
const appStateStore = useAppStateStore();
let t3;
if ($[2] !== mcp.tools || $[3] !== toolNames) {
t3 = [...toolNames, ...mcp.tools.map(_temp4)];
$[2] = mcp.tools;
$[3] = toolNames;
$[4] = t3;
} else {
t3 = $[4];
}
const combinedToolNames = t3;
let t4;
if ($[5] !== appStateStore || $[6] !== combinedToolNames) {
t4 = groupHooksByEventAndMatcher(appStateStore.getState(), combinedToolNames);
$[5] = appStateStore;
$[6] = combinedToolNames;
$[7] = t4;
} else {
t4 = $[7];
}
const hooksByEventAndMatcher = t4;
let t5;
if ($[8] !== hooksByEventAndMatcher || $[9] !== selectedEvent) {
t5 = getSortedMatchersForEvent(hooksByEventAndMatcher, selectedEvent);
$[8] = hooksByEventAndMatcher;
$[9] = selectedEvent;
$[10] = t5;
} else {
t5 = $[10];
}
const sortedMatchersForSelectedEvent = t5;
let t6;
if ($[11] !== hooksByEventAndMatcher || $[12] !== selectedEvent || $[13] !== selectedMatcher) {
t6 = getHooksForMatcher(hooksByEventAndMatcher, selectedEvent, selectedMatcher);
$[11] = hooksByEventAndMatcher;
$[12] = selectedEvent;
$[13] = selectedMatcher;
$[14] = t6;
} else {
t6 = $[14];
}
const hooksForSelectedMatcher = t6;
let t7;
if ($[15] !== onExit) {
t7 = () => {
onExit("Hooks dialog dismissed", {
display: "system"
});
};
$[15] = onExit;
$[16] = t7;
} else {
t7 = $[16];
}
const handleExit = t7;
const t8 = mode === "select-event";
let t9;
if ($[17] !== t8) {
t9 = {
context: "Confirmation",
isActive: t8
};
$[17] = t8;
$[18] = t9;
} else {
t9 = $[18];
}
useKeybinding("confirm:no", handleExit, t9);
let t10;
if ($[19] === Symbol.for("react.memo_cache_sentinel")) {
t10 = () => {
setModeState({
mode: "select-event"
});
};
$[19] = t10;
} else {
t10 = $[19];
}
const t11 = mode === "select-matcher";
let t12;
if ($[20] !== t11) {
t12 = {
context: "Confirmation",
isActive: t11
};
$[20] = t11;
$[21] = t12;
} else {
t12 = $[21];
}
useKeybinding("confirm:no", t10, t12);
let t13;
if ($[22] !== combinedToolNames || $[23] !== modeState) {
t13 = () => {
if ("event" in modeState) {
if (getMatcherMetadata(modeState.event, combinedToolNames) !== undefined) {
setModeState({
mode: "select-matcher",
event: modeState.event
});
toolNames: string[]
onExit: (
result?: string,
options?: { display?: CommandResultDisplay },
) => void
}
type ModeState =
| { mode: 'select-event' }
| { mode: 'select-matcher'; event: HookEvent }
| { mode: 'select-hook'; event: HookEvent; matcher: string }
| { mode: 'view-hook'; event: HookEvent; hook: IndividualHookConfig }
export function HooksConfigMenu({ toolNames, onExit }: Props): React.ReactNode {
const [modeState, setModeState] = useState<ModeState>({
mode: 'select-event',
})
// Cache whether hooks are disabled by policy settings.
// getSettingsForSource() is expensive (file read + JSON parse + validation),
// so we compute it once on mount and only re-compute when policy settings change.
// Short-circuit evaluation ensures we skip the expensive check when hooks aren't disabled.
const [disabledByPolicy, setDisabledByPolicy] = useState(() => {
const settings = getSettings_DEPRECATED()
const hooksDisabled = settings?.disableAllHooks === true
return (
hooksDisabled &&
getSettingsForSource('policySettings')?.disableAllHooks === true
)
})
// Check if hooks are restricted to managed-only by policy
const [restrictedByPolicy, setRestrictedByPolicy] = useState(() => {
return (
getSettingsForSource('policySettings')?.allowManagedHooksOnly === true
)
})
// Update cached values when policy settings change
useSettingsChange(source => {
if (source === 'policySettings') {
const settings = getSettings_DEPRECATED()
const hooksDisabled = settings?.disableAllHooks === true
setDisabledByPolicy(
hooksDisabled &&
getSettingsForSource('policySettings')?.disableAllHooks === true,
)
setRestrictedByPolicy(
getSettingsForSource('policySettings')?.allowManagedHooksOnly === true,
)
}
})
// Extract commonly used values from modeState for convenience
const mode = modeState.mode
const selectedEvent = 'event' in modeState ? modeState.event : 'PreToolUse'
const selectedMatcher = 'matcher' in modeState ? modeState.matcher : null
const mcp = useAppState(s => s.mcp)
const appStateStore = useAppStateStore()
const combinedToolNames = useMemo(
() => [...toolNames, ...mcp.tools.map(tool => tool.name)],
[toolNames, mcp.tools],
)
const hooksByEventAndMatcher = useMemo(
() =>
groupHooksByEventAndMatcher(appStateStore.getState(), combinedToolNames),
[combinedToolNames, appStateStore],
)
const sortedMatchersForSelectedEvent = useMemo(
() => getSortedMatchersForEvent(hooksByEventAndMatcher, selectedEvent),
[hooksByEventAndMatcher, selectedEvent],
)
const hooksForSelectedMatcher = useMemo(
() =>
getHooksForMatcher(
hooksByEventAndMatcher,
selectedEvent,
selectedMatcher,
),
[hooksByEventAndMatcher, selectedEvent, selectedMatcher],
)
// Handler for exiting the dialog
const handleExit = useCallback(() => {
onExit('Hooks dialog dismissed', { display: 'system' })
}, [onExit])
// Escape handling for select-event mode - exit the menu
useKeybinding('confirm:no', handleExit, {
context: 'Confirmation',
isActive: mode === 'select-event',
})
// Escape handling for select-matcher mode - go to select-event
useKeybinding(
'confirm:no',
() => {
setModeState({ mode: 'select-event' })
},
{
context: 'Confirmation',
isActive: mode === 'select-matcher',
},
)
// Escape handling for select-hook mode - go to select-matcher or select-event
useKeybinding(
'confirm:no',
() => {
if ('event' in modeState) {
if (
getMatcherMetadata(modeState.event, combinedToolNames) !== undefined
) {
setModeState({ mode: 'select-matcher', event: modeState.event })
} else {
setModeState({
mode: "select-event"
});
setModeState({ mode: 'select-event' })
}
}
};
$[22] = combinedToolNames;
$[23] = modeState;
$[24] = t13;
} else {
t13 = $[24];
}
const t14 = mode === "select-hook";
let t15;
if ($[25] !== t14) {
t15 = {
context: "Confirmation",
isActive: t14
};
$[25] = t14;
$[26] = t15;
} else {
t15 = $[26];
}
useKeybinding("confirm:no", t13, t15);
let t16;
if ($[27] !== modeState) {
t16 = () => {
if (modeState.mode === "view-hook") {
const {
event,
hook
} = modeState;
},
{
context: 'Confirmation',
isActive: mode === 'select-hook',
},
)
// Escape handling for view-hook mode - go to select-hook
useKeybinding(
'confirm:no',
() => {
if (modeState.mode === 'view-hook') {
const { event, hook } = modeState
setModeState({
mode: "select-hook",
mode: 'select-hook',
event,
matcher: hook.matcher || ""
});
matcher: hook.matcher || '',
})
}
};
$[27] = modeState;
$[28] = t16;
} else {
t16 = $[28];
}
const t17 = mode === "view-hook";
let t18;
if ($[29] !== t17) {
t18 = {
context: "Confirmation",
isActive: t17
};
$[29] = t17;
$[30] = t18;
} else {
t18 = $[30];
}
useKeybinding("confirm:no", t16, t18);
let t19;
if ($[31] !== combinedToolNames) {
t19 = getHookEventMetadata(combinedToolNames);
$[31] = combinedToolNames;
$[32] = t19;
} else {
t19 = $[32];
}
const hookEventMetadata = t19;
const settings_1 = getSettings_DEPRECATED();
const hooksDisabled_1 = settings_1?.disableAllHooks === true;
let t20;
if ($[33] !== hooksByEventAndMatcher) {
const byEvent = {};
let total = 0;
for (const [event_0, matchers] of Object.entries(hooksByEventAndMatcher)) {
const eventCount = Object.values(matchers).reduce(_temp5, 0);
byEvent[event_0 as HookEvent] = eventCount;
total = total + eventCount;
}
t20 = {
hooksByEvent: byEvent,
totalHooksCount: total
};
$[33] = hooksByEventAndMatcher;
$[34] = t20;
} else {
t20 = $[34];
}
const {
hooksByEvent,
totalHooksCount
} = t20;
if (hooksDisabled_1) {
let t21;
if ($[35] === Symbol.for("react.memo_cache_sentinel")) {
t21 = <Text bold={true}>disabled</Text>;
$[35] = t21;
} else {
t21 = $[35];
}
const t22 = disabledByPolicy && " by a managed settings file";
let t23;
if ($[36] !== totalHooksCount) {
t23 = <Text bold={true}>{totalHooksCount}</Text>;
$[36] = totalHooksCount;
$[37] = t23;
} else {
t23 = $[37];
}
let t24;
if ($[38] !== totalHooksCount) {
t24 = plural(totalHooksCount, "hook");
$[38] = totalHooksCount;
$[39] = t24;
} else {
t24 = $[39];
}
let t25;
if ($[40] !== totalHooksCount) {
t25 = plural(totalHooksCount, "is", "are");
$[40] = totalHooksCount;
$[41] = t25;
} else {
t25 = $[41];
}
let t26;
if ($[42] !== t22 || $[43] !== t23 || $[44] !== t24 || $[45] !== t25) {
t26 = <Text>All hooks are currently {t21}{t22}. You have{" "}{t23} configured{" "}{t24} that{" "}{t25} not running.</Text>;
$[42] = t22;
$[43] = t23;
$[44] = t24;
$[45] = t25;
$[46] = t26;
} else {
t26 = $[46];
}
let t27;
let t28;
let t29;
let t30;
if ($[47] === Symbol.for("react.memo_cache_sentinel")) {
t27 = <Box marginTop={1}><Text dimColor={true}>When hooks are disabled:</Text></Box>;
t28 = <Text dimColor={true}>· No hook commands will execute</Text>;
t29 = <Text dimColor={true}>· StatusLine will not be displayed</Text>;
t30 = <Text dimColor={true}>· Tool operations will proceed without hook validation</Text>;
$[47] = t27;
$[48] = t28;
$[49] = t29;
$[50] = t30;
} else {
t27 = $[47];
t28 = $[48];
t29 = $[49];
t30 = $[50];
}
let t31;
if ($[51] !== t26) {
t31 = <Box flexDirection="column">{t26}{t27}{t28}{t29}{t30}</Box>;
$[51] = t26;
$[52] = t31;
} else {
t31 = $[52];
}
let t32;
if ($[53] !== disabledByPolicy) {
t32 = !disabledByPolicy && <Text dimColor={true}>To re-enable hooks, remove "disableAllHooks" from settings.json or ask Claude.</Text>;
$[53] = disabledByPolicy;
$[54] = t32;
} else {
t32 = $[54];
}
let t33;
if ($[55] !== t31 || $[56] !== t32) {
t33 = <Box flexDirection="column" gap={1}>{t31}{t32}</Box>;
$[55] = t31;
$[56] = t32;
$[57] = t33;
} else {
t33 = $[57];
}
let t34;
if ($[58] !== handleExit || $[59] !== t33) {
t34 = <Dialog title="Hook Configuration - Disabled" onCancel={handleExit} inputGuide={_temp6}>{t33}</Dialog>;
$[58] = handleExit;
$[59] = t33;
$[60] = t34;
} else {
t34 = $[60];
}
return t34;
},
{
context: 'Confirmation',
isActive: mode === 'view-hook',
},
)
const hookEventMetadata = getHookEventMetadata(combinedToolNames)
// Check if hooks are disabled
const settings = getSettings_DEPRECATED()
const hooksDisabled = settings?.disableAllHooks === true
// Count hooks per event for the event-selection view, and the total.
const { hooksByEvent, totalHooksCount } = useMemo(() => {
const byEvent: Partial<Record<HookEvent, number>> = {}
let total = 0
for (const [event, matchers] of Object.entries(hooksByEventAndMatcher)) {
const eventCount = Object.values(matchers).reduce(
(sum, hooks) => sum + hooks.length,
0,
)
byEvent[event as HookEvent] = eventCount
total += eventCount
}
return { hooksByEvent: byEvent, totalHooksCount: total }
}, [hooksByEventAndMatcher])
// If hooks are disabled, show an informational screen.
// The menu is read-only, so we don't offer a re-enable button —
// users can edit settings.json or ask Claude instead.
if (hooksDisabled) {
return (
<Dialog
title="Hook Configuration - Disabled"
onCancel={handleExit}
inputGuide={() => <Text>Esc to close</Text>}
>
<Box flexDirection="column" gap={1}>
<Box flexDirection="column">
<Text>
All hooks are currently <Text bold>disabled</Text>
{disabledByPolicy && ' by a managed settings file'}. You have{' '}
<Text bold>{totalHooksCount}</Text> configured{' '}
{plural(totalHooksCount, 'hook')} that{' '}
{plural(totalHooksCount, 'is', 'are')} not running.
</Text>
<Box marginTop={1}>
<Text dimColor>When hooks are disabled:</Text>
</Box>
<Text dimColor>· No hook commands will execute</Text>
<Text dimColor>· StatusLine will not be displayed</Text>
<Text dimColor>
· Tool operations will proceed without hook validation
</Text>
</Box>
{!disabledByPolicy && (
<Text dimColor>
To re-enable hooks, remove &quot;disableAllHooks&quot; from
settings.json or ask Claude.
</Text>
)}
</Box>
</Dialog>
)
}
switch (modeState.mode) {
case "select-event":
{
let t21;
if ($[61] !== combinedToolNames) {
t21 = event_2 => {
if (getMatcherMetadata(event_2, combinedToolNames) !== undefined) {
setModeState({
mode: "select-matcher",
event: event_2
});
case 'select-event':
return (
<SelectEventMode
hookEventMetadata={hookEventMetadata}
hooksByEvent={hooksByEvent}
totalHooksCount={totalHooksCount}
restrictedByPolicy={restrictedByPolicy}
onSelectEvent={event => {
if (getMatcherMetadata(event, combinedToolNames) !== undefined) {
setModeState({ mode: 'select-matcher', event })
} else {
setModeState({
mode: "select-hook",
event: event_2,
matcher: ""
});
setModeState({ mode: 'select-hook', event, matcher: '' })
}
};
$[61] = combinedToolNames;
$[62] = t21;
} else {
t21 = $[62];
}
let t22;
if ($[63] !== handleExit || $[64] !== hookEventMetadata || $[65] !== hooksByEvent || $[66] !== restrictedByPolicy || $[67] !== t21 || $[68] !== totalHooksCount) {
t22 = <SelectEventMode hookEventMetadata={hookEventMetadata} hooksByEvent={hooksByEvent} totalHooksCount={totalHooksCount} restrictedByPolicy={restrictedByPolicy} onSelectEvent={t21} onCancel={handleExit} />;
$[63] = handleExit;
$[64] = hookEventMetadata;
$[65] = hooksByEvent;
$[66] = restrictedByPolicy;
$[67] = t21;
$[68] = totalHooksCount;
$[69] = t22;
} else {
t22 = $[69];
}
return t22;
}
case "select-matcher":
{
const t21 = hookEventMetadata[modeState.event];
let t22;
if ($[70] !== modeState.event) {
t22 = matcher => {
}}
onCancel={handleExit}
/>
)
case 'select-matcher':
return (
<SelectMatcherMode
selectedEvent={modeState.event}
matchersForSelectedEvent={sortedMatchersForSelectedEvent}
hooksByEventAndMatcher={hooksByEventAndMatcher}
eventDescription={hookEventMetadata[modeState.event].description}
onSelect={matcher => {
setModeState({
mode: "select-hook",
mode: 'select-hook',
event: modeState.event,
matcher
});
};
$[70] = modeState.event;
$[71] = t22;
} else {
t22 = $[71];
}
let t23;
if ($[72] === Symbol.for("react.memo_cache_sentinel")) {
t23 = () => {
matcher,
})
}}
onCancel={() => {
setModeState({ mode: 'select-event' })
}}
/>
)
case 'select-hook':
return (
<SelectHookMode
selectedEvent={modeState.event}
selectedMatcher={modeState.matcher}
hooksForSelectedMatcher={hooksForSelectedMatcher}
hookEventMetadata={hookEventMetadata[modeState.event]}
onSelect={hook => {
setModeState({
mode: "select-event"
});
};
$[72] = t23;
} else {
t23 = $[72];
}
let t24;
if ($[73] !== hooksByEventAndMatcher || $[74] !== modeState.event || $[75] !== sortedMatchersForSelectedEvent || $[76] !== t21.description || $[77] !== t22) {
t24 = <SelectMatcherMode selectedEvent={modeState.event} matchersForSelectedEvent={sortedMatchersForSelectedEvent} hooksByEventAndMatcher={hooksByEventAndMatcher} eventDescription={t21.description} onSelect={t22} onCancel={t23} />;
$[73] = hooksByEventAndMatcher;
$[74] = modeState.event;
$[75] = sortedMatchersForSelectedEvent;
$[76] = t21.description;
$[77] = t22;
$[78] = t24;
} else {
t24 = $[78];
}
return t24;
}
case "select-hook":
{
const t21 = hookEventMetadata[modeState.event];
let t22;
if ($[79] !== modeState.event) {
t22 = hook_1 => {
setModeState({
mode: "view-hook",
mode: 'view-hook',
event: modeState.event,
hook: hook_1
});
};
$[79] = modeState.event;
$[80] = t22;
} else {
t22 = $[80];
}
let t23;
if ($[81] !== combinedToolNames || $[82] !== modeState.event) {
t23 = () => {
if (getMatcherMetadata(modeState.event, combinedToolNames) !== undefined) {
hook,
})
}}
onCancel={() => {
// Go back to matcher selection or event selection
if (
getMatcherMetadata(modeState.event, combinedToolNames) !==
undefined
) {
setModeState({
mode: "select-matcher",
event: modeState.event
});
mode: 'select-matcher',
event: modeState.event,
})
} else {
setModeState({
mode: "select-event"
});
setModeState({ mode: 'select-event' })
}
};
$[81] = combinedToolNames;
$[82] = modeState.event;
$[83] = t23;
} else {
t23 = $[83];
}
let t24;
if ($[84] !== hooksForSelectedMatcher || $[85] !== modeState.event || $[86] !== modeState.matcher || $[87] !== t21 || $[88] !== t22 || $[89] !== t23) {
t24 = <SelectHookMode selectedEvent={modeState.event} selectedMatcher={modeState.matcher} hooksForSelectedMatcher={hooksForSelectedMatcher} hookEventMetadata={t21} onSelect={t22} onCancel={t23} />;
$[84] = hooksForSelectedMatcher;
$[85] = modeState.event;
$[86] = modeState.matcher;
$[87] = t21;
$[88] = t22;
$[89] = t23;
$[90] = t24;
} else {
t24 = $[90];
}
return t24;
}
case "view-hook":
{
const t21 = modeState.hook;
let t22;
if ($[91] !== combinedToolNames || $[92] !== modeState.event) {
t22 = getMatcherMetadata(modeState.event, combinedToolNames);
$[91] = combinedToolNames;
$[92] = modeState.event;
$[93] = t22;
} else {
t22 = $[93];
}
const t23 = t22 !== undefined;
let t24;
if ($[94] !== modeState) {
t24 = () => {
const {
event: event_1,
hook: hook_0
} = modeState;
}}
/>
)
case 'view-hook':
return (
<ViewHookMode
selectedHook={modeState.hook}
eventSupportsMatcher={
getMatcherMetadata(modeState.event, combinedToolNames) !== undefined
}
onCancel={() => {
const { event, hook } = modeState
setModeState({
mode: "select-hook",
event: event_1,
matcher: hook_0.matcher || ""
});
};
$[94] = modeState;
$[95] = t24;
} else {
t24 = $[95];
}
let t25;
if ($[96] !== modeState.hook || $[97] !== t23 || $[98] !== t24) {
t25 = <ViewHookMode selectedHook={t21} eventSupportsMatcher={t23} onCancel={t24} />;
$[96] = modeState.hook;
$[97] = t23;
$[98] = t24;
$[99] = t25;
} else {
t25 = $[99];
}
return t25;
}
mode: 'select-hook',
event,
matcher: hook.matcher || '',
})
}}
/>
)
}
}
function _temp6() {
return <Text>Esc to close</Text>;
}
function _temp5(sum, hooks) {
return sum + hooks.length;
}
function _temp4(tool) {
return tool.name;
}
function _temp3(s) {
return s.mcp;
}
function _temp2() {
return getSettingsForSource("policySettings")?.allowManagedHooksOnly === true;
}
function _temp() {
const settings = getSettings_DEPRECATED();
const hooksDisabled = settings?.disableAllHooks === true;
return hooksDisabled && getSettingsForSource("policySettings")?.disableAllHooks === true;
}

View File

@@ -1,89 +1,49 @@
import { c as _c } from "react/compiler-runtime";
import * as React from 'react';
import { Box, Text } from '../../ink.js';
import { useKeybinding } from '../../keybindings/useKeybinding.js';
import type { PromptRequest } from '../../types/hooks.js';
import { Select } from '../CustomSelect/select.js';
import { PermissionDialog } from '../permissions/PermissionDialog.js';
import * as React from 'react'
import { Box, Text } from '../../ink.js'
import { useKeybinding } from '../../keybindings/useKeybinding.js'
import type { PromptRequest } from '../../types/hooks.js'
import { Select } from '../CustomSelect/select.js'
import { PermissionDialog } from '../permissions/PermissionDialog.js'
type Props = {
title: string;
toolInputSummary?: string | null;
request: PromptRequest;
onRespond: (key: string) => void;
onAbort: () => void;
};
export function PromptDialog(t0) {
const $ = _c(15);
const {
title,
toolInputSummary,
request,
onRespond,
onAbort
} = t0;
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t1 = {
isActive: true
};
$[0] = t1;
} else {
t1 = $[0];
}
useKeybinding("app:interrupt", onAbort, t1);
let t2;
if ($[1] !== request.options) {
t2 = request.options.map(_temp);
$[1] = request.options;
$[2] = t2;
} else {
t2 = $[2];
}
const options = t2;
let t3;
if ($[3] !== toolInputSummary) {
t3 = toolInputSummary ? <Text dimColor={true}>{toolInputSummary}</Text> : undefined;
$[3] = toolInputSummary;
$[4] = t3;
} else {
t3 = $[4];
}
let t4;
if ($[5] !== onRespond) {
t4 = value => {
onRespond(value);
};
$[5] = onRespond;
$[6] = t4;
} else {
t4 = $[6];
}
let t5;
if ($[7] !== options || $[8] !== t4) {
t5 = <Box flexDirection="column" paddingY={1}><Select options={options} onChange={t4} /></Box>;
$[7] = options;
$[8] = t4;
$[9] = t5;
} else {
t5 = $[9];
}
let t6;
if ($[10] !== request.message || $[11] !== t3 || $[12] !== t5 || $[13] !== title) {
t6 = <PermissionDialog title={title} subtitle={request.message} titleRight={t3}>{t5}</PermissionDialog>;
$[10] = request.message;
$[11] = t3;
$[12] = t5;
$[13] = title;
$[14] = t6;
} else {
t6 = $[14];
}
return t6;
title: string
toolInputSummary?: string | null
request: PromptRequest
onRespond: (key: string) => void
onAbort: () => void
}
function _temp(opt) {
return {
export function PromptDialog({
title,
toolInputSummary,
request,
onRespond,
onAbort,
}: Props): React.ReactNode {
useKeybinding('app:interrupt', onAbort, { isActive: true })
const options = request.options.map(opt => ({
label: opt.label,
value: opt.key,
description: opt.description
};
description: opt.description,
}))
return (
<PermissionDialog
title={title}
subtitle={request.message}
titleRight={
toolInputSummary ? <Text dimColor>{toolInputSummary}</Text> : undefined
}
>
<Box flexDirection="column" paddingY={1}>
<Select
options={options}
onChange={value => {
onRespond(value)
}}
/>
</Box>
</PermissionDialog>
)
}

View File

@@ -1,4 +1,3 @@
import { c as _c } from "react/compiler-runtime";
/**
* SelectEventMode is the entrypoint of the Hooks config menu, where the user
* sees the list of available hook events.
@@ -8,119 +7,84 @@ import { c as _c } from "react/compiler-runtime";
* edit settings.json directly or ask Claude.
*/
import figures from 'figures';
import * as React from 'react';
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js';
import { Box, Link, Text } from '../../ink.js';
import { plural } from '../../utils/stringUtils.js';
import { Select } from '../CustomSelect/select.js';
import { Dialog } from '../design-system/Dialog.js';
import figures from 'figures'
import * as React from 'react'
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js'
import { Box, Link, Text } from '../../ink.js'
import { plural } from '../../utils/stringUtils.js'
import { Select } from '../CustomSelect/select.js'
import { Dialog } from '../design-system/Dialog.js'
type Props = {
hookEventMetadata: Record<HookEvent, HookEventMetadata>;
hooksByEvent: Partial<Record<HookEvent, number>>;
totalHooksCount: number;
restrictedByPolicy: boolean;
onSelectEvent: (event: HookEvent) => void;
onCancel: () => void;
};
export function SelectEventMode(t0) {
const $ = _c(23);
const {
hookEventMetadata,
hooksByEvent,
totalHooksCount,
restrictedByPolicy,
onSelectEvent,
onCancel
} = t0;
let t1;
if ($[0] !== totalHooksCount) {
t1 = plural(totalHooksCount, "hook");
$[0] = totalHooksCount;
$[1] = t1;
} else {
t1 = $[1];
}
const subtitle = `${totalHooksCount} ${t1} configured`;
let t2;
if ($[2] !== restrictedByPolicy) {
t2 = restrictedByPolicy && <Box flexDirection="column"><Text color="suggestion">{figures.info} Hooks Restricted by Policy</Text><Text dimColor={true}>Only hooks from managed settings can run. User-defined hooks from ~/.claude/settings.json, .claude/settings.json, and .claude/settings.local.json are blocked.</Text></Box>;
$[2] = restrictedByPolicy;
$[3] = t2;
} else {
t2 = $[3];
}
let t3;
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
t3 = <Box flexDirection="column"><Text dimColor={true}>{figures.info} This menu is read-only. To add or modify hooks, edit settings.json directly or ask Claude.{" "}<Link url="https://code.claude.com/docs/en/hooks">Learn more</Link></Text></Box>;
$[4] = t3;
} else {
t3 = $[4];
}
let t4;
if ($[5] !== onSelectEvent) {
t4 = value => {
onSelectEvent(value as HookEvent);
};
$[5] = onSelectEvent;
$[6] = t4;
} else {
t4 = $[6];
}
let t5;
if ($[7] !== hookEventMetadata) {
t5 = Object.entries(hookEventMetadata);
$[7] = hookEventMetadata;
$[8] = t5;
} else {
t5 = $[8];
}
let t6;
if ($[9] !== hooksByEvent || $[10] !== t5) {
t6 = t5.map(t7 => {
const [name, metadata] = t7;
const count = hooksByEvent[name as HookEvent] || 0;
return {
label: count > 0 ? <Text>{name} <Text color="suggestion">({count})</Text></Text> : name,
value: name,
description: metadata.summary
};
});
$[9] = hooksByEvent;
$[10] = t5;
$[11] = t6;
} else {
t6 = $[11];
}
let t7;
if ($[12] !== onCancel || $[13] !== t4 || $[14] !== t6) {
t7 = <Box flexDirection="column"><Select onChange={t4} onCancel={onCancel} options={t6} /></Box>;
$[12] = onCancel;
$[13] = t4;
$[14] = t6;
$[15] = t7;
} else {
t7 = $[15];
}
let t8;
if ($[16] !== t2 || $[17] !== t7) {
t8 = <Box flexDirection="column" gap={1}>{t2}{t3}{t7}</Box>;
$[16] = t2;
$[17] = t7;
$[18] = t8;
} else {
t8 = $[18];
}
let t9;
if ($[19] !== onCancel || $[20] !== subtitle || $[21] !== t8) {
t9 = <Dialog title="Hooks" subtitle={subtitle} onCancel={onCancel}>{t8}</Dialog>;
$[19] = onCancel;
$[20] = subtitle;
$[21] = t8;
$[22] = t9;
} else {
t9 = $[22];
}
return t9;
hookEventMetadata: Record<HookEvent, HookEventMetadata>
hooksByEvent: Partial<Record<HookEvent, number>>
totalHooksCount: number
restrictedByPolicy: boolean
onSelectEvent: (event: HookEvent) => void
onCancel: () => void
}
export function SelectEventMode({
hookEventMetadata,
hooksByEvent,
totalHooksCount,
restrictedByPolicy,
onSelectEvent,
onCancel,
}: Props): React.ReactNode {
const subtitle = `${totalHooksCount} ${plural(totalHooksCount, 'hook')} configured`
return (
<Dialog title="Hooks" subtitle={subtitle} onCancel={onCancel}>
<Box flexDirection="column" gap={1}>
{restrictedByPolicy && (
<Box flexDirection="column">
<Text color="suggestion">
{figures.info} Hooks Restricted by Policy
</Text>
<Text dimColor>
Only hooks from managed settings can run. User-defined hooks from
~/.claude/settings.json, .claude/settings.json, and
.claude/settings.local.json are blocked.
</Text>
</Box>
)}
<Box flexDirection="column">
<Text dimColor>
{figures.info} This menu is read-only. To add or modify hooks, edit
settings.json directly or ask Claude.{' '}
<Link url="https://code.claude.com/docs/en/hooks">Learn more</Link>
</Text>
</Box>
<Box flexDirection="column">
<Select
onChange={value => {
onSelectEvent(value as HookEvent)
}}
onCancel={onCancel}
options={Object.entries(hookEventMetadata).map(
([name, metadata]) => {
const count = hooksByEvent[name as HookEvent] || 0
return {
label:
count > 0 ? (
<Text>
{name} <Text color="suggestion">({count})</Text>
</Text>
) : (
name
),
value: name,
description: metadata.summary,
}
},
)}
/>
</Box>
</Box>
</Dialog>
)
}

View File

@@ -1,4 +1,3 @@
import { c as _c } from "react/compiler-runtime";
/**
* SelectHookMode shows all hooks configured for a given event+matcher pair.
*
@@ -6,106 +5,84 @@ import { c as _c } from "react/compiler-runtime";
* and selecting a hook shows its read-only details instead of a delete
* confirmation.
*/
import * as React from 'react';
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js';
import { Box, Text } from '../../ink.js';
import { getHookDisplayText, hookSourceHeaderDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
import { Select } from '../CustomSelect/select.js';
import { Dialog } from '../design-system/Dialog.js';
import * as React from 'react'
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js'
import { Box, Text } from '../../ink.js'
import {
getHookDisplayText,
hookSourceHeaderDisplayString,
type IndividualHookConfig,
} from '../../utils/hooks/hooksSettings.js'
import { Select } from '../CustomSelect/select.js'
import { Dialog } from '../design-system/Dialog.js'
type Props = {
selectedEvent: HookEvent;
selectedMatcher: string | null;
hooksForSelectedMatcher: IndividualHookConfig[];
hookEventMetadata: HookEventMetadata;
onSelect: (hook: IndividualHookConfig) => void;
onCancel: () => void;
};
export function SelectHookMode(t0) {
const $ = _c(19);
const {
selectedEvent,
selectedMatcher,
hooksForSelectedMatcher,
hookEventMetadata,
onSelect,
onCancel
} = t0;
const title = hookEventMetadata.matcherMetadata !== undefined ? `${selectedEvent} - Matcher: ${selectedMatcher || "(all)"}` : selectedEvent;
selectedEvent: HookEvent
selectedMatcher: string | null
hooksForSelectedMatcher: IndividualHookConfig[]
hookEventMetadata: HookEventMetadata
onSelect: (hook: IndividualHookConfig) => void
onCancel: () => void
}
export function SelectHookMode({
selectedEvent,
selectedMatcher,
hooksForSelectedMatcher,
hookEventMetadata,
onSelect,
onCancel,
}: Props): React.ReactNode {
const title =
hookEventMetadata.matcherMetadata !== undefined
? `${selectedEvent} - Matcher: ${selectedMatcher || '(all)'}`
: selectedEvent
if (hooksForSelectedMatcher.length === 0) {
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t1 = <Box flexDirection="column" gap={1}><Text dimColor={true}>No hooks configured for this event.</Text><Text dimColor={true}>To add hooks, edit settings.json directly or ask Claude.</Text></Box>;
$[0] = t1;
} else {
t1 = $[0];
}
let t2;
if ($[1] !== hookEventMetadata.description || $[2] !== onCancel || $[3] !== title) {
t2 = <Dialog title={title} subtitle={hookEventMetadata.description} onCancel={onCancel} inputGuide={_temp}>{t1}</Dialog>;
$[1] = hookEventMetadata.description;
$[2] = onCancel;
$[3] = title;
$[4] = t2;
} else {
t2 = $[4];
}
return t2;
return (
<Dialog
title={title}
subtitle={hookEventMetadata.description}
onCancel={onCancel}
inputGuide={() => <Text>Esc to go back</Text>}
>
<Box flexDirection="column" gap={1}>
<Text dimColor>No hooks configured for this event.</Text>
<Text dimColor>
To add hooks, edit settings.json directly or ask Claude.
</Text>
</Box>
</Dialog>
)
}
const t1 = hookEventMetadata.description;
let t2;
if ($[5] !== hooksForSelectedMatcher) {
t2 = hooksForSelectedMatcher.map(_temp2);
$[5] = hooksForSelectedMatcher;
$[6] = t2;
} else {
t2 = $[6];
}
let t3;
if ($[7] !== hooksForSelectedMatcher || $[8] !== onSelect) {
t3 = value => {
const index_0 = parseInt(value, 10);
const hook_0 = hooksForSelectedMatcher[index_0];
if (hook_0) {
onSelect(hook_0);
}
};
$[7] = hooksForSelectedMatcher;
$[8] = onSelect;
$[9] = t3;
} else {
t3 = $[9];
}
let t4;
if ($[10] !== onCancel || $[11] !== t2 || $[12] !== t3) {
t4 = <Box flexDirection="column"><Select options={t2} onChange={t3} onCancel={onCancel} /></Box>;
$[10] = onCancel;
$[11] = t2;
$[12] = t3;
$[13] = t4;
} else {
t4 = $[13];
}
let t5;
if ($[14] !== hookEventMetadata.description || $[15] !== onCancel || $[16] !== t4 || $[17] !== title) {
t5 = <Dialog title={title} subtitle={t1} onCancel={onCancel}>{t4}</Dialog>;
$[14] = hookEventMetadata.description;
$[15] = onCancel;
$[16] = t4;
$[17] = title;
$[18] = t5;
} else {
t5 = $[18];
}
return t5;
}
function _temp2(hook, index) {
return {
label: `[${hook.config.type}] ${getHookDisplayText(hook.config)}`,
value: index.toString(),
description: hook.source === "pluginHook" && hook.pluginName ? `${hookSourceHeaderDisplayString(hook.source)} (${hook.pluginName})` : hookSourceHeaderDisplayString(hook.source)
};
}
function _temp() {
return <Text>Esc to go back</Text>;
return (
<Dialog
title={title}
subtitle={hookEventMetadata.description}
onCancel={onCancel}
>
<Box flexDirection="column">
<Select
options={hooksForSelectedMatcher.map((hook, index) => ({
label: `[${hook.config.type}] ${getHookDisplayText(hook.config)}`,
value: index.toString(),
description:
hook.source === 'pluginHook' && hook.pluginName
? `${hookSourceHeaderDisplayString(hook.source)} (${hook.pluginName})`
: hookSourceHeaderDisplayString(hook.source),
}))}
onChange={value => {
const index = parseInt(value, 10)
const hook = hooksForSelectedMatcher[index]
if (hook) {
onSelect(hook)
}
}}
onCancel={onCancel}
/>
</Box>
</Dialog>
)
}

View File

@@ -1,143 +1,103 @@
import { c as _c } from "react/compiler-runtime";
/**
* SelectMatcherMode shows the configured matchers for a selected hook event.
*
* The /hooks menu is read-only: this view no longer offers "add new matcher"
* and simply lets the user drill into each matcher to see its hooks.
*/
import * as React from 'react';
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
import { Box, Text } from '../../ink.js';
import { type HookSource, hookSourceInlineDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
import { plural } from '../../utils/stringUtils.js';
import { Select } from '../CustomSelect/select.js';
import { Dialog } from '../design-system/Dialog.js';
import * as React from 'react'
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
import { Box, Text } from '../../ink.js'
import {
type HookSource,
hookSourceInlineDisplayString,
type IndividualHookConfig,
} from '../../utils/hooks/hooksSettings.js'
import { plural } from '../../utils/stringUtils.js'
import { Select } from '../CustomSelect/select.js'
import { Dialog } from '../design-system/Dialog.js'
type MatcherWithSource = {
matcher: string;
sources: HookSource[];
hookCount: number;
};
matcher: string
sources: HookSource[]
hookCount: number
}
type Props = {
selectedEvent: HookEvent;
matchersForSelectedEvent: string[];
hooksByEventAndMatcher: Record<HookEvent, Record<string, IndividualHookConfig[]>>;
eventDescription: string;
onSelect: (matcher: string) => void;
onCancel: () => void;
};
export function SelectMatcherMode(t0) {
const $ = _c(25);
const {
selectedEvent,
matchersForSelectedEvent,
hooksByEventAndMatcher,
eventDescription,
onSelect,
onCancel
} = t0;
let t1;
if ($[0] !== hooksByEventAndMatcher || $[1] !== matchersForSelectedEvent || $[2] !== selectedEvent) {
let t2;
if ($[4] !== hooksByEventAndMatcher || $[5] !== selectedEvent) {
t2 = matcher => {
const hooks = hooksByEventAndMatcher[selectedEvent]?.[matcher] || [];
const sources = Array.from(new Set(hooks.map(_temp)));
return {
matcher,
sources,
hookCount: hooks.length
};
};
$[4] = hooksByEventAndMatcher;
$[5] = selectedEvent;
$[6] = t2;
} else {
t2 = $[6];
}
t1 = matchersForSelectedEvent.map(t2);
$[0] = hooksByEventAndMatcher;
$[1] = matchersForSelectedEvent;
$[2] = selectedEvent;
$[3] = t1;
} else {
t1 = $[3];
}
const matchersWithSources = t1;
selectedEvent: HookEvent
matchersForSelectedEvent: string[]
hooksByEventAndMatcher: Record<
HookEvent,
Record<string, IndividualHookConfig[]>
>
eventDescription: string
onSelect: (matcher: string) => void
onCancel: () => void
}
export function SelectMatcherMode({
selectedEvent,
matchersForSelectedEvent,
hooksByEventAndMatcher,
eventDescription,
onSelect,
onCancel,
}: Props): React.ReactNode {
// Group matchers with their sources (already sorted by priority in parent)
const matchersWithSources: MatcherWithSource[] = React.useMemo(() => {
return matchersForSelectedEvent.map(matcher => {
const hooks = hooksByEventAndMatcher[selectedEvent]?.[matcher] || []
const sources = Array.from(new Set(hooks.map(h => h.source)))
return {
matcher,
sources,
hookCount: hooks.length,
}
})
}, [matchersForSelectedEvent, hooksByEventAndMatcher, selectedEvent])
if (matchersForSelectedEvent.length === 0) {
const t2 = `${selectedEvent} - Matchers`;
let t3;
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
t3 = <Box flexDirection="column" gap={1}><Text dimColor={true}>No hooks configured for this event.</Text><Text dimColor={true}>To add hooks, edit settings.json directly or ask Claude.</Text></Box>;
$[7] = t3;
} else {
t3 = $[7];
}
let t4;
if ($[8] !== eventDescription || $[9] !== onCancel || $[10] !== t2) {
t4 = <Dialog title={t2} subtitle={eventDescription} onCancel={onCancel} inputGuide={_temp2}>{t3}</Dialog>;
$[8] = eventDescription;
$[9] = onCancel;
$[10] = t2;
$[11] = t4;
} else {
t4 = $[11];
}
return t4;
return (
<Dialog
title={`${selectedEvent} - Matchers`}
subtitle={eventDescription}
onCancel={onCancel}
inputGuide={() => <Text>Esc to go back</Text>}
>
<Box flexDirection="column" gap={1}>
<Text dimColor>No hooks configured for this event.</Text>
<Text dimColor>
To add hooks, edit settings.json directly or ask Claude.
</Text>
</Box>
</Dialog>
)
}
const t2 = `${selectedEvent} - Matchers`;
let t3;
if ($[12] !== matchersWithSources) {
t3 = matchersWithSources.map(_temp3);
$[12] = matchersWithSources;
$[13] = t3;
} else {
t3 = $[13];
}
let t4;
if ($[14] !== onSelect) {
t4 = value => {
onSelect(value);
};
$[14] = onSelect;
$[15] = t4;
} else {
t4 = $[15];
}
let t5;
if ($[16] !== onCancel || $[17] !== t3 || $[18] !== t4) {
t5 = <Box flexDirection="column"><Select options={t3} onChange={t4} onCancel={onCancel} /></Box>;
$[16] = onCancel;
$[17] = t3;
$[18] = t4;
$[19] = t5;
} else {
t5 = $[19];
}
let t6;
if ($[20] !== eventDescription || $[21] !== onCancel || $[22] !== t2 || $[23] !== t5) {
t6 = <Dialog title={t2} subtitle={eventDescription} onCancel={onCancel}>{t5}</Dialog>;
$[20] = eventDescription;
$[21] = onCancel;
$[22] = t2;
$[23] = t5;
$[24] = t6;
} else {
t6 = $[24];
}
return t6;
}
function _temp3(item) {
const sourceText = item.sources.map(hookSourceInlineDisplayString).join(", ");
const matcherLabel = item.matcher || "(all)";
return {
label: `[${sourceText}] ${matcherLabel}`,
value: item.matcher,
description: `${item.hookCount} ${plural(item.hookCount, "hook")}`
};
}
function _temp2() {
return <Text>Esc to go back</Text>;
}
function _temp(h) {
return h.source;
return (
<Dialog
title={`${selectedEvent} - Matchers`}
subtitle={eventDescription}
onCancel={onCancel}
>
<Box flexDirection="column">
<Select
options={matchersWithSources.map(item => {
const sourceText = item.sources
.map(hookSourceInlineDisplayString)
.join(', ')
const matcherLabel = item.matcher || '(all)'
return {
label: `[${sourceText}] ${matcherLabel}`,
value: item.matcher,
description: `${item.hookCount} ${plural(item.hookCount, 'hook')}`,
}
})}
onChange={value => {
onSelect(value)
}}
onCancel={onCancel}
/>
</Box>
</Dialog>
)
}

View File

@@ -1,182 +1,100 @@
import { c as _c } from "react/compiler-runtime";
/**
* ViewHookMode shows read-only details for a single configured hook.
*
* The /hooks menu is read-only; this view replaces the former delete-hook
* confirmation screen and directs users to settings.json or Claude for edits.
*/
import * as React from 'react';
import { Box, Text } from '../../ink.js';
import { hookSourceDescriptionDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
import { Dialog } from '../design-system/Dialog.js';
import * as React from 'react'
import { Box, Text } from '../../ink.js'
import {
hookSourceDescriptionDisplayString,
type IndividualHookConfig,
} from '../../utils/hooks/hooksSettings.js'
import { Dialog } from '../design-system/Dialog.js'
type Props = {
selectedHook: IndividualHookConfig;
eventSupportsMatcher: boolean;
onCancel: () => void;
};
export function ViewHookMode(t0) {
const $ = _c(40);
const {
selectedHook,
eventSupportsMatcher,
onCancel
} = t0;
let t1;
if ($[0] !== selectedHook.event) {
t1 = <Text>Event: <Text bold={true}>{selectedHook.event}</Text></Text>;
$[0] = selectedHook.event;
$[1] = t1;
} else {
t1 = $[1];
}
let t2;
if ($[2] !== eventSupportsMatcher || $[3] !== selectedHook.matcher) {
t2 = eventSupportsMatcher && <Text>Matcher: <Text bold={true}>{selectedHook.matcher || "(all)"}</Text></Text>;
$[2] = eventSupportsMatcher;
$[3] = selectedHook.matcher;
$[4] = t2;
} else {
t2 = $[4];
}
let t3;
if ($[5] !== selectedHook.config.type) {
t3 = <Text>Type: <Text bold={true}>{selectedHook.config.type}</Text></Text>;
$[5] = selectedHook.config.type;
$[6] = t3;
} else {
t3 = $[6];
}
let t4;
if ($[7] !== selectedHook.source) {
t4 = hookSourceDescriptionDisplayString(selectedHook.source);
$[7] = selectedHook.source;
$[8] = t4;
} else {
t4 = $[8];
}
let t5;
if ($[9] !== t4) {
t5 = <Text>Source:{" "}<Text dimColor={true}>{t4}</Text></Text>;
$[9] = t4;
$[10] = t5;
} else {
t5 = $[10];
}
let t6;
if ($[11] !== selectedHook.pluginName) {
t6 = selectedHook.pluginName && <Text>Plugin: <Text dimColor={true}>{selectedHook.pluginName}</Text></Text>;
$[11] = selectedHook.pluginName;
$[12] = t6;
} else {
t6 = $[12];
}
let t7;
if ($[13] !== t1 || $[14] !== t2 || $[15] !== t3 || $[16] !== t5 || $[17] !== t6) {
t7 = <Box flexDirection="column">{t1}{t2}{t3}{t5}{t6}</Box>;
$[13] = t1;
$[14] = t2;
$[15] = t3;
$[16] = t5;
$[17] = t6;
$[18] = t7;
} else {
t7 = $[18];
}
let t8;
if ($[19] !== selectedHook.config) {
t8 = getContentFieldLabel(selectedHook.config);
$[19] = selectedHook.config;
$[20] = t8;
} else {
t8 = $[20];
}
let t9;
if ($[21] !== t8) {
t9 = <Text dimColor={true}>{t8}:</Text>;
$[21] = t8;
$[22] = t9;
} else {
t9 = $[22];
}
let t10;
if ($[23] !== selectedHook.config) {
t10 = getContentFieldValue(selectedHook.config);
$[23] = selectedHook.config;
$[24] = t10;
} else {
t10 = $[24];
}
let t11;
if ($[25] !== t10) {
t11 = <Box borderStyle="round" borderDimColor={true} paddingLeft={1} paddingRight={1}><Text>{t10}</Text></Box>;
$[25] = t10;
$[26] = t11;
} else {
t11 = $[26];
}
let t12;
if ($[27] !== t11 || $[28] !== t9) {
t12 = <Box flexDirection="column">{t9}{t11}</Box>;
$[27] = t11;
$[28] = t9;
$[29] = t12;
} else {
t12 = $[29];
}
let t13;
if ($[30] !== selectedHook.config) {
t13 = "statusMessage" in selectedHook.config && selectedHook.config.statusMessage && <Text>Status message:{" "}<Text dimColor={true}>{selectedHook.config.statusMessage}</Text></Text>;
$[30] = selectedHook.config;
$[31] = t13;
} else {
t13 = $[31];
}
let t14;
if ($[32] === Symbol.for("react.memo_cache_sentinel")) {
t14 = <Text dimColor={true}>To modify or remove this hook, edit settings.json directly or ask Claude to help.</Text>;
$[32] = t14;
} else {
t14 = $[32];
}
let t15;
if ($[33] !== t12 || $[34] !== t13 || $[35] !== t7) {
t15 = <Box flexDirection="column" gap={1}>{t7}{t12}{t13}{t14}</Box>;
$[33] = t12;
$[34] = t13;
$[35] = t7;
$[36] = t15;
} else {
t15 = $[36];
}
let t16;
if ($[37] !== onCancel || $[38] !== t15) {
t16 = <Dialog title="Hook details" onCancel={onCancel} inputGuide={_temp}>{t15}</Dialog>;
$[37] = onCancel;
$[38] = t15;
$[39] = t16;
} else {
t16 = $[39];
}
return t16;
selectedHook: IndividualHookConfig
eventSupportsMatcher: boolean
onCancel: () => void
}
export function ViewHookMode({
selectedHook,
eventSupportsMatcher,
onCancel,
}: Props): React.ReactNode {
return (
<Dialog
title="Hook details"
onCancel={onCancel}
inputGuide={() => <Text>Esc to go back</Text>}
>
<Box flexDirection="column" gap={1}>
<Box flexDirection="column">
<Text>
Event: <Text bold>{selectedHook.event}</Text>
</Text>
{eventSupportsMatcher && (
<Text>
Matcher: <Text bold>{selectedHook.matcher || '(all)'}</Text>
</Text>
)}
<Text>
Type: <Text bold>{selectedHook.config.type}</Text>
</Text>
<Text>
Source:{' '}
<Text dimColor>
{hookSourceDescriptionDisplayString(selectedHook.source)}
</Text>
</Text>
{selectedHook.pluginName && (
<Text>
Plugin: <Text dimColor>{selectedHook.pluginName}</Text>
</Text>
)}
</Box>
<Box flexDirection="column">
<Text dimColor>{getContentFieldLabel(selectedHook.config)}:</Text>
<Box
borderStyle="round"
borderDimColor
paddingLeft={1}
paddingRight={1}
>
<Text>{getContentFieldValue(selectedHook.config)}</Text>
</Box>
</Box>
{'statusMessage' in selectedHook.config &&
selectedHook.config.statusMessage && (
<Text>
Status message:{' '}
<Text dimColor>{selectedHook.config.statusMessage}</Text>
</Text>
)}
<Text dimColor>
To modify or remove this hook, edit settings.json directly or ask
Claude to help.
</Text>
</Box>
</Dialog>
)
}
/**
* Get a human-readable label for the primary content field of a hook
* based on its type.
*/
function _temp() {
return <Text>Esc to go back</Text>;
}
function getContentFieldLabel(config: IndividualHookConfig['config']): string {
switch (config.type) {
case 'command':
return 'Command';
return 'Command'
case 'prompt':
return 'Prompt';
return 'Prompt'
case 'agent':
return 'Prompt';
return 'Prompt'
case 'http':
return 'URL';
return 'URL'
}
}
@@ -187,12 +105,12 @@ function getContentFieldLabel(config: IndividualHookConfig['config']): string {
function getContentFieldValue(config: IndividualHookConfig['config']): string {
switch (config.type) {
case 'command':
return config.command;
return config.command
case 'prompt':
return config.prompt;
return config.prompt
case 'agent':
return config.prompt;
return config.prompt
case 'http':
return config.url;
return config.url
}
}