mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-15 02:42:45 +08:00
feat(docs): add full documentation generation phase and related documentation generation phase
- Implement Phase 4: Full Documentation Generation with multi-layered strategy and tool fallback. - Introduce Phase 5: Related Documentation Generation for incremental updates based on git changes. - Create new utility components for displaying execution status in the terminal panel. - Add helper functions for rendering execution status icons and formatting relative time. - Establish a recent paths configuration for improved path resolution.
This commit is contained in:
@@ -11,9 +11,7 @@ import {
|
||||
Play,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
Clock,
|
||||
Terminal,
|
||||
Loader2,
|
||||
} from 'lucide-react';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
@@ -23,50 +21,9 @@ import {
|
||||
selectExecutionStats,
|
||||
useTerminalPanelStore,
|
||||
} from '@/stores';
|
||||
import type { QueueExecution, QueueExecutionStatus } from '@/stores/queueExecutionStore';
|
||||
import type { QueueExecution } from '@/stores/queueExecutionStore';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// ========== Helpers ==========
|
||||
|
||||
function statusBadgeVariant(status: QueueExecutionStatus): 'info' | 'success' | 'destructive' | 'secondary' {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return 'info';
|
||||
case 'completed':
|
||||
return 'success';
|
||||
case 'failed':
|
||||
return 'destructive';
|
||||
case 'pending':
|
||||
default:
|
||||
return 'secondary';
|
||||
}
|
||||
}
|
||||
|
||||
function statusIcon(status: QueueExecutionStatus) {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return <Loader2 className="w-3.5 h-3.5 animate-spin" />;
|
||||
case 'completed':
|
||||
return <CheckCircle className="w-3.5 h-3.5" />;
|
||||
case 'failed':
|
||||
return <XCircle className="w-3.5 h-3.5" />;
|
||||
case 'pending':
|
||||
default:
|
||||
return <Clock className="w-3.5 h-3.5" />;
|
||||
}
|
||||
}
|
||||
|
||||
function formatRelativeTime(isoString: string): string {
|
||||
const diff = Date.now() - new Date(isoString).getTime();
|
||||
const seconds = Math.floor(diff / 1000);
|
||||
if (seconds < 60) return `${seconds}s ago`;
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
if (minutes < 60) return `${minutes}m ago`;
|
||||
const hours = Math.floor(minutes / 60);
|
||||
if (hours < 24) return `${hours}h ago`;
|
||||
const days = Math.floor(hours / 24);
|
||||
return `${days}d ago`;
|
||||
}
|
||||
import { statusIcon, statusBadgeVariant, formatRelativeTime } from '@/lib/execution-display-utils';
|
||||
|
||||
// ========== Empty State ==========
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
} from '@/components/shared/CliExecutionSettings';
|
||||
import { buildQueueItemContext } from '@/lib/queue-prompt';
|
||||
import { useQueueExecutionStore, type QueueExecution } from '@/stores/queueExecutionStore';
|
||||
import type { Flow } from '@/types/flow';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
@@ -70,7 +71,7 @@ export function QueueItemExecutor({ item, className }: QueueItemExecutorProps) {
|
||||
// Resolve the parent issue for context building
|
||||
const { issues } = useIssues();
|
||||
const issue = useMemo(
|
||||
() => issues.find((i) => i.id === item.issue_id) as any,
|
||||
() => issues.find((i) => i.id === item.issue_id),
|
||||
[issues, item.issue_id]
|
||||
);
|
||||
|
||||
@@ -203,13 +204,13 @@ export function QueueItemExecutor({ item, className }: QueueItemExecutorProps) {
|
||||
throw new Error('Failed to create flow');
|
||||
}
|
||||
|
||||
// Hydrate Orchestrator stores
|
||||
const flowDto = created.data as any;
|
||||
// Hydrate Orchestrator stores -- convert OrchestratorFlowDto to Flow
|
||||
const flowDto = created.data;
|
||||
const parsedVersion = parseInt(String(flowDto.version ?? '1'), 10);
|
||||
const flowForStore = {
|
||||
const flowForStore: Flow = {
|
||||
...flowDto,
|
||||
version: Number.isFinite(parsedVersion) ? parsedVersion : 1,
|
||||
} as any;
|
||||
} as Flow;
|
||||
useFlowStore.getState().setCurrentFlow(flowForStore);
|
||||
|
||||
// Execute the flow
|
||||
|
||||
@@ -59,7 +59,7 @@ export interface CcwConfig {
|
||||
enabledTools: string[];
|
||||
projectRoot?: string;
|
||||
allowedDirs?: string;
|
||||
disableSandbox?: boolean;
|
||||
enableSandbox?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +75,7 @@ export interface CcwToolsMcpCardProps {
|
||||
/** Comma-separated list of allowed directories */
|
||||
allowedDirs?: string;
|
||||
/** Whether sandbox is disabled */
|
||||
disableSandbox?: boolean;
|
||||
enableSandbox?: boolean;
|
||||
/** Callback when a tool is toggled */
|
||||
onToggleTool: (tool: string, enabled: boolean) => void;
|
||||
/** Callback when configuration is updated */
|
||||
@@ -110,7 +110,7 @@ export function CcwToolsMcpCard({
|
||||
enabledTools,
|
||||
projectRoot,
|
||||
allowedDirs,
|
||||
disableSandbox,
|
||||
enableSandbox,
|
||||
onToggleTool,
|
||||
onUpdateConfig,
|
||||
onInstall,
|
||||
@@ -123,7 +123,7 @@ export function CcwToolsMcpCard({
|
||||
// Local state for config inputs
|
||||
const [projectRootInput, setProjectRootInput] = useState(projectRoot || '');
|
||||
const [allowedDirsInput, setAllowedDirsInput] = useState(allowedDirs || '');
|
||||
const [disableSandboxInput, setDisableSandboxInput] = useState(disableSandbox || false);
|
||||
const [enableSandboxInput, setEnableSandboxInput] = useState(enableSandbox || false);
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [installScope, setInstallScope] = useState<'global' | 'project'>('global');
|
||||
|
||||
@@ -193,7 +193,7 @@ export function CcwToolsMcpCard({
|
||||
updateConfigMutation.mutate({
|
||||
projectRoot: projectRootInput || undefined,
|
||||
allowedDirs: allowedDirsInput || undefined,
|
||||
disableSandbox: disableSandboxInput,
|
||||
enableSandbox: enableSandboxInput,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -387,22 +387,22 @@ export function CcwToolsMcpCard({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Disable Sandbox */}
|
||||
{/* Enable Sandbox */}
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="ccw-disable-sandbox"
|
||||
checked={disableSandboxInput}
|
||||
onChange={(e) => setDisableSandboxInput(e.target.checked)}
|
||||
id="ccw-enable-sandbox"
|
||||
checked={enableSandboxInput}
|
||||
onChange={(e) => setEnableSandboxInput(e.target.checked)}
|
||||
disabled={!isInstalled}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
<label
|
||||
htmlFor="ccw-disable-sandbox"
|
||||
htmlFor="ccw-enable-sandbox"
|
||||
className="text-sm text-foreground flex items-center gap-1 cursor-pointer"
|
||||
>
|
||||
<Shield className="w-4 h-4" />
|
||||
{formatMessage({ id: 'mcp.ccw.paths.disableSandbox' })}
|
||||
{formatMessage({ id: 'mcp.ccw.paths.enableSandbox' })}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
// ========================================
|
||||
// QueueExecutionListView Component
|
||||
// ========================================
|
||||
// Compact execution list for TerminalPanel queue view.
|
||||
// Subscribes to queueExecutionStore and renders execution entries
|
||||
// with status badges, tool/mode labels, and relative timestamps.
|
||||
// Click navigates to terminal view (session) or orchestrator page.
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { ClipboardList } from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useQueueExecutionStore } from '@/stores/queueExecutionStore';
|
||||
import { useTerminalPanelStore } from '@/stores/terminalPanelStore';
|
||||
import type { QueueExecution } from '@/stores/queueExecutionStore';
|
||||
import { ROUTES } from '@/router';
|
||||
import { statusIcon, statusBadgeVariant, formatRelativeTime } from '@/lib/execution-display-utils';
|
||||
|
||||
// ========== Execution Item ==========
|
||||
|
||||
function QueueExecutionItem({
|
||||
execution,
|
||||
onClick,
|
||||
}: {
|
||||
execution: QueueExecution;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const typeLabel = execution.type === 'session'
|
||||
? formatMessage({ id: 'home.terminalPanel.queueView.session' })
|
||||
: formatMessage({ id: 'home.terminalPanel.queueView.orchestrator' });
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'w-full text-left px-3 py-2.5 rounded-md transition-colors',
|
||||
'hover:bg-muted/60 focus:outline-none focus:ring-1 focus:ring-primary/30'
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
{statusIcon(execution.status)}
|
||||
<span className="text-sm font-medium font-mono text-foreground truncate">
|
||||
{execution.queueItemId}
|
||||
</span>
|
||||
</div>
|
||||
<Badge variant={statusBadgeVariant(execution.status)} className="shrink-0 text-[10px] px-1.5 py-0">
|
||||
{formatMessage({ id: `home.terminalPanel.status.${execution.status}` })}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="mt-1 flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<span className="font-mono">{execution.tool}</span>
|
||||
<span className="text-border">|</span>
|
||||
<span>{execution.mode}</span>
|
||||
<span className="text-border">|</span>
|
||||
<span>{typeLabel}</span>
|
||||
<span className="ml-auto shrink-0">{formatRelativeTime(execution.startedAt)}</span>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
// ========== Empty State ==========
|
||||
|
||||
function QueueEmptyState() {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex items-center justify-center text-muted-foreground">
|
||||
<div className="text-center">
|
||||
<ClipboardList className="h-12 w-12 mx-auto mb-4 opacity-50" />
|
||||
<p className="text-sm">{formatMessage({ id: 'home.terminalPanel.queueView.emptyTitle' })}</p>
|
||||
<p className="text-xs mt-1">{formatMessage({ id: 'home.terminalPanel.queueView.emptyDesc' })}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ========== Main Component ==========
|
||||
|
||||
export function QueueExecutionListView() {
|
||||
const navigate = useNavigate();
|
||||
const executions = useQueueExecutionStore((s) => s.executions);
|
||||
const setPanelView = useTerminalPanelStore((s) => s.setPanelView);
|
||||
const openTerminal = useTerminalPanelStore((s) => s.openTerminal);
|
||||
|
||||
// Sort: running first, then pending, then failed, then completed; within same status by startedAt desc
|
||||
const sortedExecutions = useMemo(() => {
|
||||
const all = Object.values(executions);
|
||||
const statusOrder: Record<string, number> = {
|
||||
running: 0,
|
||||
pending: 1,
|
||||
failed: 2,
|
||||
completed: 3,
|
||||
};
|
||||
return all.sort((a, b) => {
|
||||
const sa = statusOrder[a.status] ?? 4;
|
||||
const sb = statusOrder[b.status] ?? 4;
|
||||
if (sa !== sb) return sa - sb;
|
||||
return new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime();
|
||||
});
|
||||
}, [executions]);
|
||||
|
||||
const handleClick = (exec: QueueExecution) => {
|
||||
if (exec.type === 'session' && exec.sessionKey) {
|
||||
setPanelView('terminal');
|
||||
openTerminal(exec.sessionKey);
|
||||
} else {
|
||||
navigate(ROUTES.ORCHESTRATOR);
|
||||
}
|
||||
};
|
||||
|
||||
if (sortedExecutions.length === 0) {
|
||||
return <QueueEmptyState />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col min-h-0 overflow-y-auto p-2 space-y-0.5">
|
||||
{sortedExecutions.map((exec) => (
|
||||
<QueueExecutionItem
|
||||
key={exec.id}
|
||||
execution={exec}
|
||||
onClick={() => handleClick(exec)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -201,8 +201,9 @@ export function TerminalMainArea({ onClose }: TerminalMainAreaProps) {
|
||||
category: 'user',
|
||||
}, projectPath || undefined);
|
||||
setPrompt('');
|
||||
} catch {
|
||||
// Error shown in terminal output
|
||||
} catch (err) {
|
||||
// Error shown in terminal output; log for DevTools debugging
|
||||
console.error('[TerminalMainArea] executeInCliSession failed:', err);
|
||||
} finally {
|
||||
setIsExecuting(false);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ export interface UseCliSessionCoreOptions {
|
||||
autoSelectLast?: boolean;
|
||||
/** Default resumeKey used when creating sessions via ensureSession/handleCreateSession. */
|
||||
resumeKey?: string;
|
||||
/** Shell to use when creating new sessions. Defaults to 'bash'. */
|
||||
preferredShell?: string;
|
||||
/** Additional createCliSession fields (cols, rows, tool, model). */
|
||||
createSessionDefaults?: {
|
||||
cols?: number;
|
||||
@@ -60,6 +62,7 @@ export function useCliSessionCore(options: UseCliSessionCoreOptions = {}): UseCl
|
||||
const {
|
||||
autoSelectLast = true,
|
||||
resumeKey,
|
||||
preferredShell = 'bash',
|
||||
createSessionDefaults,
|
||||
} = options;
|
||||
|
||||
@@ -92,7 +95,7 @@ export function useCliSessionCore(options: UseCliSessionCoreOptions = {}): UseCl
|
||||
setError(null);
|
||||
try {
|
||||
const r = await fetchCliSessions(projectPath || undefined);
|
||||
setSessions(r.sessions as unknown as CliSession[]);
|
||||
setSessions(r.sessions);
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : String(e));
|
||||
} finally {
|
||||
@@ -120,16 +123,16 @@ export function useCliSessionCore(options: UseCliSessionCoreOptions = {}): UseCl
|
||||
const created = await createCliSession(
|
||||
{
|
||||
workingDir: projectPath,
|
||||
preferredShell: 'bash',
|
||||
preferredShell,
|
||||
resumeKey,
|
||||
...createSessionDefaults,
|
||||
},
|
||||
projectPath
|
||||
);
|
||||
upsertSession(created.session as unknown as CliSession);
|
||||
upsertSession(created.session);
|
||||
setSelectedSessionKey(created.session.sessionKey);
|
||||
return created.session.sessionKey;
|
||||
}, [selectedSessionKey, projectPath, resumeKey, createSessionDefaults, upsertSession]);
|
||||
}, [selectedSessionKey, projectPath, resumeKey, preferredShell, createSessionDefaults, upsertSession]);
|
||||
|
||||
// ------- handleCreateSession -------
|
||||
const handleCreateSession = useCallback(async () => {
|
||||
@@ -139,21 +142,21 @@ export function useCliSessionCore(options: UseCliSessionCoreOptions = {}): UseCl
|
||||
const created = await createCliSession(
|
||||
{
|
||||
workingDir: projectPath,
|
||||
preferredShell: 'bash',
|
||||
preferredShell,
|
||||
resumeKey,
|
||||
...createSessionDefaults,
|
||||
},
|
||||
projectPath
|
||||
);
|
||||
upsertSession(created.session as unknown as CliSession);
|
||||
upsertSession(created.session);
|
||||
setSelectedSessionKey(created.session.sessionKey);
|
||||
// Refresh full list so store stays consistent
|
||||
const r = await fetchCliSessions(projectPath || undefined);
|
||||
setSessions(r.sessions as unknown as CliSession[]);
|
||||
setSessions(r.sessions);
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : String(e));
|
||||
}
|
||||
}, [projectPath, resumeKey, createSessionDefaults, upsertSession, setSessions]);
|
||||
}, [projectPath, resumeKey, preferredShell, createSessionDefaults, upsertSession, setSessions]);
|
||||
|
||||
return {
|
||||
sessions,
|
||||
|
||||
65
ccw/frontend/src/lib/execution-display-utils.ts
Normal file
65
ccw/frontend/src/lib/execution-display-utils.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
// ========================================
|
||||
// Execution Display Utilities
|
||||
// ========================================
|
||||
// Shared helpers for rendering queue execution status across
|
||||
// ExecutionPanel and QueueExecutionListView components.
|
||||
|
||||
import type { ReactElement } from 'react';
|
||||
import { createElement } from 'react';
|
||||
import {
|
||||
Loader2,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
Clock,
|
||||
} from 'lucide-react';
|
||||
import type { QueueExecutionStatus } from '@/stores/queueExecutionStore';
|
||||
|
||||
/**
|
||||
* Map execution status to Badge variant.
|
||||
*/
|
||||
export function statusBadgeVariant(status: QueueExecutionStatus): 'info' | 'success' | 'destructive' | 'secondary' {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return 'info';
|
||||
case 'completed':
|
||||
return 'success';
|
||||
case 'failed':
|
||||
return 'destructive';
|
||||
case 'pending':
|
||||
default:
|
||||
return 'secondary';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map execution status to a small icon element.
|
||||
*/
|
||||
export function statusIcon(status: QueueExecutionStatus): ReactElement {
|
||||
const base = 'w-3.5 h-3.5';
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return createElement(Loader2, { className: `${base} animate-spin text-info` });
|
||||
case 'completed':
|
||||
return createElement(CheckCircle, { className: `${base} text-success` });
|
||||
case 'failed':
|
||||
return createElement(XCircle, { className: `${base} text-destructive` });
|
||||
case 'pending':
|
||||
default:
|
||||
return createElement(Clock, { className: `${base} text-muted-foreground` });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format an ISO date string as a human-readable relative time.
|
||||
*/
|
||||
export function formatRelativeTime(isoString: string): string {
|
||||
const diff = Date.now() - new Date(isoString).getTime();
|
||||
const seconds = Math.floor(diff / 1000);
|
||||
if (seconds < 60) return `${seconds}s ago`;
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
if (minutes < 60) return `${minutes}m ago`;
|
||||
const hours = Math.floor(minutes / 60);
|
||||
if (hours < 24) return `${hours}h ago`;
|
||||
const days = Math.floor(hours / 24);
|
||||
return `${days}d ago`;
|
||||
}
|
||||
@@ -7,6 +7,34 @@
|
||||
|
||||
import type { QueueItem } from '@/lib/api';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Minimal interfaces for the issue data consumed by buildQueueItemContext.
|
||||
// The backend may return solution.tasks[] which is not part of the core
|
||||
// IssueSolution interface, so we define a local superset here.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** A lightweight task entry nested inside a solution from the backend. */
|
||||
interface SolutionTask {
|
||||
id: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/** Solution shape as consumed by the prompt builder (superset of IssueSolution). */
|
||||
interface PromptSolution {
|
||||
id: string;
|
||||
description?: string;
|
||||
approach?: string;
|
||||
tasks?: SolutionTask[];
|
||||
}
|
||||
|
||||
/** Minimal issue shape consumed by the prompt builder. */
|
||||
export interface QueueItemIssue {
|
||||
title?: string;
|
||||
context?: string;
|
||||
solutions?: PromptSolution[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a context string for executing a queue item.
|
||||
*
|
||||
@@ -18,7 +46,7 @@ import type { QueueItem } from '@/lib/api';
|
||||
*/
|
||||
export function buildQueueItemContext(
|
||||
item: QueueItem,
|
||||
issue: any | undefined
|
||||
issue: QueueItemIssue | undefined
|
||||
): string {
|
||||
const lines: string[] = [];
|
||||
|
||||
@@ -38,7 +66,7 @@ export function buildQueueItemContext(
|
||||
}
|
||||
|
||||
const solution = Array.isArray(issue.solutions)
|
||||
? issue.solutions.find((s: any) => s?.id === item.solution_id)
|
||||
? issue.solutions.find((s) => s?.id === item.solution_id)
|
||||
: undefined;
|
||||
|
||||
if (solution) {
|
||||
@@ -54,7 +82,7 @@ export function buildQueueItemContext(
|
||||
// Include matched task from solution.tasks when available
|
||||
const tasks = Array.isArray(solution.tasks) ? solution.tasks : [];
|
||||
const task = item.task_id
|
||||
? tasks.find((t: any) => t?.id === item.task_id)
|
||||
? tasks.find((t) => t?.id === item.task_id)
|
||||
: undefined;
|
||||
if (task) {
|
||||
lines.push('');
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
"allowedDirs": "Allowed Directories",
|
||||
"allowedDirsPlaceholder": "dir1,dir2,dir3",
|
||||
"allowedDirsHint": "Comma-separated list of allowed directories",
|
||||
"disableSandbox": "Disable Sandbox"
|
||||
"enableSandbox": "Enable Sandbox (restrict to workspace files only)"
|
||||
},
|
||||
"actions": {
|
||||
"enableAll": "Enable All",
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
"allowedDirs": "允许的目录",
|
||||
"allowedDirsPlaceholder": "目录1,目录2,目录3",
|
||||
"allowedDirsHint": "逗号分隔的允许目录列表",
|
||||
"disableSandbox": "禁用沙箱"
|
||||
"enableSandbox": "启用沙箱(仅允许修改工作空间下的文件)"
|
||||
},
|
||||
"actions": {
|
||||
"enableAll": "全部启用",
|
||||
|
||||
@@ -349,7 +349,7 @@ export function McpManagerPage() {
|
||||
enabledTools: [],
|
||||
projectRoot: undefined,
|
||||
allowedDirs: undefined,
|
||||
disableSandbox: undefined,
|
||||
enableSandbox: undefined,
|
||||
};
|
||||
|
||||
const handleToggleCcwTool = async (tool: string, enabled: boolean) => {
|
||||
@@ -407,7 +407,7 @@ export function McpManagerPage() {
|
||||
enabledTools: [],
|
||||
projectRoot: undefined,
|
||||
allowedDirs: undefined,
|
||||
disableSandbox: undefined,
|
||||
enableSandbox: undefined,
|
||||
};
|
||||
|
||||
const handleToggleCcwToolCodex = async (tool: string, enabled: boolean) => {
|
||||
@@ -721,7 +721,7 @@ export function McpManagerPage() {
|
||||
enabledTools={ccwConfig.enabledTools}
|
||||
projectRoot={ccwConfig.projectRoot}
|
||||
allowedDirs={ccwConfig.allowedDirs}
|
||||
disableSandbox={ccwConfig.disableSandbox}
|
||||
enableSandbox={ccwConfig.enableSandbox}
|
||||
onToggleTool={handleToggleCcwTool}
|
||||
onUpdateConfig={handleUpdateCcwConfig}
|
||||
onInstall={handleCcwInstall}
|
||||
@@ -734,7 +734,7 @@ export function McpManagerPage() {
|
||||
enabledTools={ccwCodexConfig.enabledTools}
|
||||
projectRoot={ccwCodexConfig.projectRoot}
|
||||
allowedDirs={ccwCodexConfig.allowedDirs}
|
||||
disableSandbox={ccwCodexConfig.disableSandbox}
|
||||
enableSandbox={ccwCodexConfig.enableSandbox}
|
||||
onToggleTool={handleToggleCcwToolCodex}
|
||||
onUpdateConfig={handleUpdateCcwConfigCodex}
|
||||
onInstall={handleCcwInstallCodex}
|
||||
|
||||
Reference in New Issue
Block a user