mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
feat: Add orchestrator template and roles for executor and planner
- Created a new orchestrator template for Codex skill design, detailing structure and execution phases. - Introduced the executor role with responsibilities for task execution, including routing to backends and handling implementation. - Added the planner role for requirement breakdown, issue creation, and task dispatching, ensuring a structured planning process.
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
AlertCircle,
|
||||
ListChecks,
|
||||
Info,
|
||||
FolderOpen,
|
||||
LayoutGrid,
|
||||
Columns2,
|
||||
Rows2,
|
||||
@@ -47,7 +48,7 @@ import { CliConfigModal, type CliSessionConfig } from './CliConfigModal';
|
||||
|
||||
// ========== Types ==========
|
||||
|
||||
export type PanelId = 'issues' | 'queue' | 'inspector';
|
||||
export type PanelId = 'issues' | 'queue' | 'inspector' | 'files';
|
||||
|
||||
interface DashboardToolbarProps {
|
||||
activePanel: PanelId | null;
|
||||
@@ -292,6 +293,12 @@ export function DashboardToolbar({ activePanel, onTogglePanel }: DashboardToolba
|
||||
onClick={() => onTogglePanel('inspector')}
|
||||
dot={hasChain}
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={FolderOpen}
|
||||
label={formatMessage({ id: 'terminalDashboard.toolbar.files', defaultMessage: 'Files' })}
|
||||
isActive={activePanel === 'files'}
|
||||
onClick={() => onTogglePanel('files')}
|
||||
/>
|
||||
|
||||
{/* Separator */}
|
||||
<div className="w-px h-5 bg-border mx-1" />
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface FloatingFileBrowserProps {
|
||||
rootPath: string;
|
||||
onInsertPath?: (path: string) => void;
|
||||
initialSelectedPath?: string | null;
|
||||
width?: number | string;
|
||||
}
|
||||
|
||||
export function FloatingFileBrowser({
|
||||
@@ -28,6 +29,7 @@ export function FloatingFileBrowser({
|
||||
rootPath,
|
||||
onInsertPath,
|
||||
initialSelectedPath = null,
|
||||
width = 400,
|
||||
}: FloatingFileBrowserProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
@@ -89,7 +91,7 @@ export function FloatingFileBrowser({
|
||||
onClose={onClose}
|
||||
title={formatMessage({ id: 'terminalDashboard.fileBrowser.title' })}
|
||||
side="right"
|
||||
width={400}
|
||||
width={width}
|
||||
>
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Toolbar */}
|
||||
@@ -148,7 +150,7 @@ export function FloatingFileBrowser({
|
||||
{/* Body */}
|
||||
<div className="flex-1 min-h-0 flex overflow-hidden">
|
||||
{/* Tree */}
|
||||
<div className="w-[180px] shrink-0 border-r border-border overflow-y-auto">
|
||||
<div className="w-[240px] shrink-0 border-r border-border overflow-y-auto">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center py-8 text-muted-foreground">
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
|
||||
@@ -15,7 +15,7 @@ interface FloatingPanelProps {
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
side?: 'left' | 'right';
|
||||
width?: number;
|
||||
width?: number | string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ export function FloatingPanel({
|
||||
style={{
|
||||
top: '40px', // Below toolbar
|
||||
height: 'calc(100vh - 40px)', // Full height below toolbar
|
||||
width: `${width}px`,
|
||||
width: typeof width === 'number' ? `${width}px` : width,
|
||||
}}
|
||||
>
|
||||
{/* Panel header */}
|
||||
|
||||
@@ -82,8 +82,9 @@ function IssueItem({
|
||||
);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={cn(
|
||||
'w-full text-left px-2.5 py-1.5 rounded-md transition-colors',
|
||||
'hover:bg-muted/60 focus:outline-none focus:ring-1 focus:ring-primary/30',
|
||||
@@ -91,6 +92,7 @@ function IssueItem({
|
||||
isHighlighted && !isSelected && 'bg-accent/50'
|
||||
)}
|
||||
onClick={onSelect}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onSelect(); } }}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
@@ -129,7 +131,7 @@ function IssueItem({
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
"issues": "Issues",
|
||||
"queue": "Queue",
|
||||
"inspector": "Inspector",
|
||||
"files": "Files",
|
||||
"layoutSingle": "Single",
|
||||
"layoutSplitH": "Split Horizontal",
|
||||
"layoutSplitV": "Split Vertical",
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
"issues": "问题",
|
||||
"queue": "队列",
|
||||
"inspector": "检查器",
|
||||
"files": "文件",
|
||||
"layoutSingle": "单窗格",
|
||||
"layoutSplitH": "左右分割",
|
||||
"layoutSplitV": "上下分割",
|
||||
|
||||
@@ -18,6 +18,10 @@ import { AgentList } from '@/components/terminal-dashboard/AgentList';
|
||||
import { IssuePanel } from '@/components/terminal-dashboard/IssuePanel';
|
||||
import { QueuePanel } from '@/components/terminal-dashboard/QueuePanel';
|
||||
import { InspectorContent } from '@/components/terminal-dashboard/BottomInspector';
|
||||
import { FloatingFileBrowser } from '@/components/terminal-dashboard/FloatingFileBrowser';
|
||||
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
||||
import { useTerminalGridStore, selectTerminalGridFocusedPaneId } from '@/stores/terminalGridStore';
|
||||
import { sendCliSessionText } from '@/lib/api';
|
||||
|
||||
// ========== Main Page Component ==========
|
||||
|
||||
@@ -25,6 +29,10 @@ export function TerminalDashboardPage() {
|
||||
const { formatMessage } = useIntl();
|
||||
const [activePanel, setActivePanel] = useState<PanelId | null>(null);
|
||||
|
||||
const projectPath = useWorkflowStore(selectProjectPath);
|
||||
const focusedPaneId = useTerminalGridStore(selectTerminalGridFocusedPaneId);
|
||||
const panes = useTerminalGridStore((s) => s.panes);
|
||||
|
||||
const togglePanel = useCallback((panelId: PanelId) => {
|
||||
setActivePanel((prev) => (prev === panelId ? null : panelId));
|
||||
}, []);
|
||||
@@ -33,6 +41,20 @@ export function TerminalDashboardPage() {
|
||||
setActivePanel(null);
|
||||
}, []);
|
||||
|
||||
const handleInsertPath = useCallback(
|
||||
(path: string) => {
|
||||
if (!focusedPaneId) return;
|
||||
const sessionId = panes[focusedPaneId]?.sessionId;
|
||||
if (!sessionId) return;
|
||||
sendCliSessionText(
|
||||
sessionId,
|
||||
{ text: path, appendNewline: false },
|
||||
projectPath ?? undefined
|
||||
).catch((err) => console.error('[TerminalDashboard] insert path failed:', err));
|
||||
},
|
||||
[focusedPaneId, panes, projectPath]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="-m-4 md:-m-6 flex flex-col h-[calc(100vh-56px)] overflow-hidden">
|
||||
<AssociationHighlightProvider>
|
||||
@@ -90,6 +112,15 @@ export function TerminalDashboardPage() {
|
||||
>
|
||||
<InspectorContent />
|
||||
</FloatingPanel>
|
||||
|
||||
{/* File browser (half screen, right side) */}
|
||||
<FloatingFileBrowser
|
||||
isOpen={activePanel === 'files'}
|
||||
onClose={closePanel}
|
||||
rootPath={projectPath ?? '/'}
|
||||
onInsertPath={focusedPaneId ? handleInsertPath : undefined}
|
||||
width="50vw"
|
||||
/>
|
||||
</AssociationHighlightProvider>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -235,6 +235,10 @@ export const useConfigStore = create<ConfigStore>()(
|
||||
secondaryModel: t.secondaryModel || '',
|
||||
tags: t.tags || [],
|
||||
type: t.type || 'builtin',
|
||||
// Load additional fields from backend (fixes cross-browser config sync)
|
||||
envFile: t.envFile,
|
||||
settingsFile: t.settingsFile,
|
||||
availableModels: t.availableModels,
|
||||
};
|
||||
}
|
||||
if (Object.keys(cliTools).length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user