@assistkick/create 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/create.js +0 -0
- package/package.json +9 -7
- package/templates/assistkick-product-system/.env.example +1 -0
- package/templates/assistkick-product-system/local.db +0 -0
- package/templates/assistkick-product-system/package.json +4 -2
- package/templates/assistkick-product-system/packages/backend/package.json +2 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +165 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +358 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +356 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +96 -1
- package/templates/assistkick-product-system/packages/backend/src/routes/graph.ts +1 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +43 -4
- package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +200 -84
- package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +6 -3
- package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +53 -17
- package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +218 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +119 -0
- package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +154 -0
- package/templates/assistkick-product-system/packages/backend/src/server.ts +81 -9
- package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +489 -0
- package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +416 -0
- package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.test.ts +189 -0
- package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.ts +182 -0
- package/templates/assistkick-product-system/packages/backend/src/services/init.ts +28 -78
- package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +16 -0
- package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +73 -2
- package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +4 -4
- package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +87 -11
- package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +210 -69
- package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +210 -215
- package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +162 -0
- package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +148 -0
- package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +11 -5
- package/templates/assistkick-product-system/packages/backend/src/services/tts_service.test.ts +64 -0
- package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +134 -0
- package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.test.ts +256 -0
- package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +258 -0
- package/templates/assistkick-product-system/packages/backend/src/services/workflow_group_service.ts +106 -0
- package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.test.ts +275 -0
- package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.ts +222 -0
- package/templates/assistkick-product-system/packages/frontend/package-lock.json +3455 -0
- package/templates/assistkick-product-system/packages/frontend/package.json +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/App.tsx +8 -0
- package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +456 -16
- package/templates/assistkick-product-system/packages/frontend/src/api/client_files.test.ts +172 -0
- package/templates/assistkick-product-system/packages/frontend/src/api/client_video.test.ts +238 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +307 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/CoherenceView.tsx +82 -66
- package/templates/assistkick-product-system/packages/frontend/src/components/CompositionPlaceholder.tsx +97 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/DesignSystemView.tsx +20 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +57 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +313 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeContextMenu.tsx +61 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +73 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +404 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/GitRepoModal.tsx +187 -56
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +71 -73
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphSettings.tsx +8 -8
- package/templates/assistkick-product-system/packages/frontend/src/components/GraphView.tsx +1 -1
- package/templates/assistkick-product-system/packages/frontend/src/components/InviteUserDialog.tsx +15 -11
- package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +202 -171
- package/templates/assistkick-product-system/packages/frontend/src/components/LoginPage.tsx +14 -14
- package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +54 -33
- package/templates/assistkick-product-system/packages/frontend/src/components/QaIssueSheet.tsx +32 -49
- package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +43 -48
- package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +121 -52
- package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +20 -14
- package/templates/assistkick-product-system/packages/frontend/src/components/UsersView.tsx +52 -52
- package/templates/assistkick-product-system/packages/frontend/src/components/VideoGallery.tsx +313 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/VideographyView.tsx +250 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/WorkflowsView.tsx +474 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/AccentBorderList.tsx +53 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/Button.tsx +87 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonGroup.tsx +29 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonShowcase.tsx +221 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/CardGlass.tsx +141 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/CompletionRing.tsx +30 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/ContentCard.tsx +34 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/IconButton.tsx +74 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCard.tsx +103 -87
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +9 -188
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/Kbd.tsx +11 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/KindBadge.tsx +21 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/NavBarSidekick.tsx +81 -37
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/SidePanelShowcase.tsx +370 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/SideSheet.tsx +64 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/ds/StatusDot.tsx +18 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCardPositionNode.tsx +36 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCycleCountNode.tsx +60 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/EndNode.tsx +42 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +189 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +123 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RunAgentNode.tsx +51 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/SetCardMetadataNode.tsx +53 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/StartNode.tsx +18 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/TransitionCardNode.tsx +59 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +335 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +634 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/autoLayout.ts +103 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/edgeColors.ts +35 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +208 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.test.ts +119 -0
- package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +107 -0
- package/templates/assistkick-product-system/packages/frontend/src/constants/graph.ts +13 -11
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useAutoSave.ts +75 -0
- package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
- package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
- package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
- package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
- package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
- package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +1 -1
- package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +2 -2
- package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -2
- package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
- package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
- package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +6 -3
- package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +4 -4
- package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +275 -3535
- package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
- package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
- package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
- package/templates/assistkick-product-system/packages/frontend/vite.config.ts +5 -0
- package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
- package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
- package/templates/assistkick-product-system/packages/shared/db/schema.ts +113 -0
- package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
- package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
- package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
- package/templates/assistkick-product-system/packages/shared/lib/graph.ts +5 -0
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1753 -0
- package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1281 -0
- package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
- package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
- package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
- package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
- package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
- package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
- package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
- package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
- package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
- package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
- package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
- package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
- package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
- package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
- package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
- package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
- package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
- package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
- package/templates/assistkick-product-system/packages/video/index.ts +4 -0
- package/templates/assistkick-product-system/packages/video/package.json +28 -0
- package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
- package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
- package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
- package/templates/assistkick-product-system/packages/video/style.css +1 -0
- package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
- package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
- package/templates/assistkick-product-system/tests/video_render_service.test.ts +179 -0
- package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
- package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
- package/templates/skills/assistkick-developer/SKILL.md +3 -0
- package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
- package/templates/skills/product-system/graph.json +1890 -0
- package/templates/skills/product-system/kanban.json +304 -0
- package/templates/skills/product-system/nodes/comp_001.md +56 -0
- package/templates/skills/product-system/nodes/comp_002.md +57 -0
- package/templates/skills/product-system/nodes/data_001.md +51 -0
- package/templates/skills/product-system/nodes/data_002.md +40 -0
- package/templates/skills/product-system/nodes/data_004.md +38 -0
- package/templates/skills/product-system/nodes/dec_001.md +34 -0
- package/templates/skills/product-system/nodes/dec_016.md +32 -0
- package/templates/skills/product-system/nodes/feat_008.md +30 -0
- package/templates/skills/video-composition-agent/SKILL.md +232 -0
- package/templates/skills/video-script-writer/SKILL.md +136 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agents View — management UI for agent prompts.
|
|
3
|
+
* Supports global and project-scoped views with create, edit, delete, and reset.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
7
|
+
import { apiClient } from '../api/client';
|
|
8
|
+
import { useProjectStore } from '../stores/useProjectStore';
|
|
9
|
+
import { Button } from './ds/Button';
|
|
10
|
+
import { Plus, RotateCcw, Trash2, Save, ChevronDown } from 'lucide-react';
|
|
11
|
+
|
|
12
|
+
interface Agent {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
promptTemplate: string;
|
|
16
|
+
projectId: string | null;
|
|
17
|
+
isDefault: number;
|
|
18
|
+
createdAt: string;
|
|
19
|
+
updatedAt: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const PLACEHOLDER_DOCS = [
|
|
23
|
+
{ name: '{{featureId}}', description: 'The feature ID being worked on', stages: 'All' },
|
|
24
|
+
{ name: '{{projectId}}', description: 'The current project ID', stages: 'All' },
|
|
25
|
+
{ name: '{{mainToolsDir}}', description: 'Absolute path to the tools directory', stages: 'All' },
|
|
26
|
+
{ name: '{{mainSkillDir}}', description: 'Absolute path to shared data directory', stages: 'All' },
|
|
27
|
+
{ name: '{{pidFlag}}', description: 'The --project-id flag string (or empty)', stages: 'All' },
|
|
28
|
+
{ name: '{{cycle}}', description: 'Current development cycle number', stages: 'Developer' },
|
|
29
|
+
{ name: '{{previousReviewNotes}}', description: 'Review feedback from prior cycle', stages: 'Developer' },
|
|
30
|
+
{ name: '{{debuggerFindings}}', description: 'Root cause analysis from debugger', stages: 'Developer' },
|
|
31
|
+
{ name: '{{unaddressedNotes}}', description: 'QA rejection notes to investigate', stages: 'Debugger' },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export function AgentsView() {
|
|
35
|
+
const projects = useProjectStore((s) => s.projects);
|
|
36
|
+
const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
|
|
37
|
+
|
|
38
|
+
const [scope, setScope] = useState<'global' | 'project'>('global');
|
|
39
|
+
const [projectId, setProjectId] = useState(selectedProjectId || '');
|
|
40
|
+
const [agents, setAgents] = useState<Agent[]>([]);
|
|
41
|
+
const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
|
|
42
|
+
const [editName, setEditName] = useState('');
|
|
43
|
+
const [editPrompt, setEditPrompt] = useState('');
|
|
44
|
+
const [loading, setLoading] = useState(true);
|
|
45
|
+
const [saving, setSaving] = useState(false);
|
|
46
|
+
const [error, setError] = useState('');
|
|
47
|
+
const [successMsg, setSuccessMsg] = useState('');
|
|
48
|
+
|
|
49
|
+
const fetchData = useCallback(async () => {
|
|
50
|
+
setLoading(true);
|
|
51
|
+
setError('');
|
|
52
|
+
try {
|
|
53
|
+
const pid = scope === 'project' ? projectId : undefined;
|
|
54
|
+
const agentsResp = await apiClient.fetchAgents(scope, pid);
|
|
55
|
+
setAgents(agentsResp.agents);
|
|
56
|
+
} catch (err: any) {
|
|
57
|
+
setError(err.message || 'Failed to load agents');
|
|
58
|
+
} finally {
|
|
59
|
+
setLoading(false);
|
|
60
|
+
}
|
|
61
|
+
}, [scope, projectId]);
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
fetchData();
|
|
65
|
+
}, [fetchData]);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (selectedProjectId) setProjectId(selectedProjectId);
|
|
69
|
+
}, [selectedProjectId]);
|
|
70
|
+
|
|
71
|
+
const showSuccess = (msg: string) => {
|
|
72
|
+
setSuccessMsg(msg);
|
|
73
|
+
setTimeout(() => setSuccessMsg(''), 3000);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const selectAgent = (agent: Agent) => {
|
|
77
|
+
setSelectedAgent(agent);
|
|
78
|
+
setEditName(agent.name);
|
|
79
|
+
setEditPrompt(agent.promptTemplate);
|
|
80
|
+
setError('');
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const handleCreate = async () => {
|
|
84
|
+
try {
|
|
85
|
+
const data: any = {
|
|
86
|
+
name: 'New Agent',
|
|
87
|
+
promptTemplate: 'You are a helpful agent.\n\nFeature: {{featureId}}\nProject: {{projectId}}',
|
|
88
|
+
};
|
|
89
|
+
if (scope === 'project' && projectId) data.projectId = projectId;
|
|
90
|
+
const resp = await apiClient.createAgent(data);
|
|
91
|
+
await fetchData();
|
|
92
|
+
selectAgent(resp.agent);
|
|
93
|
+
showSuccess('Agent created');
|
|
94
|
+
} catch (err: any) {
|
|
95
|
+
setError(err.message);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const handleSave = async () => {
|
|
100
|
+
if (!selectedAgent) return;
|
|
101
|
+
setSaving(true);
|
|
102
|
+
setError('');
|
|
103
|
+
try {
|
|
104
|
+
const resp = await apiClient.updateAgent(selectedAgent.id, {
|
|
105
|
+
name: editName.trim(),
|
|
106
|
+
promptTemplate: editPrompt,
|
|
107
|
+
});
|
|
108
|
+
setSelectedAgent(resp.agent);
|
|
109
|
+
await fetchData();
|
|
110
|
+
showSuccess('Agent saved');
|
|
111
|
+
} catch (err: any) {
|
|
112
|
+
setError(err.message);
|
|
113
|
+
} finally {
|
|
114
|
+
setSaving(false);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const handleDelete = async () => {
|
|
119
|
+
if (!selectedAgent) return;
|
|
120
|
+
setError('');
|
|
121
|
+
try {
|
|
122
|
+
await apiClient.deleteAgent(selectedAgent.id);
|
|
123
|
+
setSelectedAgent(null);
|
|
124
|
+
await fetchData();
|
|
125
|
+
showSuccess('Agent deleted');
|
|
126
|
+
} catch (err: any) {
|
|
127
|
+
setError(err.message);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const handleReset = async () => {
|
|
132
|
+
if (!selectedAgent) return;
|
|
133
|
+
setError('');
|
|
134
|
+
try {
|
|
135
|
+
const resp = await apiClient.resetAgent(selectedAgent.id);
|
|
136
|
+
selectAgent(resp.agent);
|
|
137
|
+
await fetchData();
|
|
138
|
+
showSuccess('Agent reset to default');
|
|
139
|
+
} catch (err: any) {
|
|
140
|
+
setError(err.message);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const hasChanges = selectedAgent && (editName !== selectedAgent.name || editPrompt !== selectedAgent.promptTemplate);
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<div className="flex h-full">
|
|
148
|
+
{/* Left Panel — Agent List */}
|
|
149
|
+
<div className="w-80 border-r border-edge flex flex-col shrink-0">
|
|
150
|
+
{/* Scope Toggle */}
|
|
151
|
+
<div className="p-3 border-b border-edge space-y-2">
|
|
152
|
+
<div className="flex gap-1 p-0.5 rounded-lg bg-surface-raised">
|
|
153
|
+
<button
|
|
154
|
+
onClick={() => setScope('global')}
|
|
155
|
+
className={`flex-1 px-3 py-1.5 rounded-md text-[12px] font-medium transition-all ${
|
|
156
|
+
scope === 'global'
|
|
157
|
+
? 'bg-accent text-surface shadow-sm'
|
|
158
|
+
: 'text-content-muted hover:text-content'
|
|
159
|
+
}`}
|
|
160
|
+
>
|
|
161
|
+
Global
|
|
162
|
+
</button>
|
|
163
|
+
<button
|
|
164
|
+
onClick={() => setScope('project')}
|
|
165
|
+
className={`flex-1 px-3 py-1.5 rounded-md text-[12px] font-medium transition-all ${
|
|
166
|
+
scope === 'project'
|
|
167
|
+
? 'bg-accent text-surface shadow-sm'
|
|
168
|
+
: 'text-content-muted hover:text-content'
|
|
169
|
+
}`}
|
|
170
|
+
>
|
|
171
|
+
Project
|
|
172
|
+
</button>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
{scope === 'project' && (
|
|
176
|
+
<div className="relative">
|
|
177
|
+
<select
|
|
178
|
+
value={projectId}
|
|
179
|
+
onChange={(e) => setProjectId(e.target.value)}
|
|
180
|
+
className="w-full px-3 py-1.5 rounded-md border border-edge bg-surface text-[12px] text-content appearance-none pr-7"
|
|
181
|
+
>
|
|
182
|
+
{projects.map(p => (
|
|
183
|
+
<option key={p.id} value={p.id}>{p.name}</option>
|
|
184
|
+
))}
|
|
185
|
+
</select>
|
|
186
|
+
<ChevronDown size={12} className="absolute right-2 top-1/2 -translate-y-1/2 text-content-muted pointer-events-none" />
|
|
187
|
+
</div>
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
{/* Agent List */}
|
|
192
|
+
<div className="flex-1 overflow-y-auto">
|
|
193
|
+
{loading ? (
|
|
194
|
+
<div className="p-4 text-center text-[12px] text-content-muted">Loading...</div>
|
|
195
|
+
) : agents.length === 0 ? (
|
|
196
|
+
<div className="p-4 text-center text-[12px] text-content-muted">No agents found</div>
|
|
197
|
+
) : (
|
|
198
|
+
agents.map(agent => {
|
|
199
|
+
const isSelected = selectedAgent?.id === agent.id;
|
|
200
|
+
return (
|
|
201
|
+
<button
|
|
202
|
+
key={agent.id}
|
|
203
|
+
onClick={() => selectAgent(agent)}
|
|
204
|
+
className={`w-full text-left px-3 py-2.5 border-b border-edge transition-all ${
|
|
205
|
+
isSelected
|
|
206
|
+
? 'bg-accent/10 border-l-2 border-l-accent'
|
|
207
|
+
: 'hover:bg-surface-raised border-l-2 border-l-transparent'
|
|
208
|
+
}`}
|
|
209
|
+
>
|
|
210
|
+
<div className="flex items-center gap-2">
|
|
211
|
+
<span className="text-[13px] font-medium text-content truncate">{agent.name}</span>
|
|
212
|
+
{agent.isDefault === 1 && (
|
|
213
|
+
<span className="shrink-0 px-1.5 py-0.5 rounded text-[9px] font-semibold uppercase bg-accent/10 text-accent">Default</span>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
</button>
|
|
217
|
+
);
|
|
218
|
+
})
|
|
219
|
+
)}
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
{/* Create Agent Button */}
|
|
223
|
+
<div className="p-3 border-t border-edge">
|
|
224
|
+
<Button variant="primary" size="sm" icon={<Plus size={12} />} onClick={handleCreate} className="w-full justify-center">
|
|
225
|
+
Create Agent
|
|
226
|
+
</Button>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
{/* Right Panel — Agent Detail */}
|
|
231
|
+
<div className="flex-1 flex flex-col overflow-hidden">
|
|
232
|
+
{error && (
|
|
233
|
+
<div className="mx-4 mt-3 px-3 py-2 rounded-md bg-error/10 border border-error/20 text-error text-[12px] flex items-center justify-between">
|
|
234
|
+
<span>{error}</span>
|
|
235
|
+
<button onClick={() => setError('')} className="text-error hover:text-error/80">×</button>
|
|
236
|
+
</div>
|
|
237
|
+
)}
|
|
238
|
+
{successMsg && (
|
|
239
|
+
<div className="mx-4 mt-3 px-3 py-2 rounded-md bg-accent/10 border border-accent/20 text-accent text-[12px]">
|
|
240
|
+
{successMsg}
|
|
241
|
+
</div>
|
|
242
|
+
)}
|
|
243
|
+
|
|
244
|
+
{!selectedAgent ? (
|
|
245
|
+
<div className="flex-1 flex items-center justify-center text-content-muted text-[13px]">
|
|
246
|
+
Select an agent to view details
|
|
247
|
+
</div>
|
|
248
|
+
) : (
|
|
249
|
+
<>
|
|
250
|
+
{/* Header */}
|
|
251
|
+
<div className="p-4 border-b border-edge flex items-center justify-between gap-3">
|
|
252
|
+
<input
|
|
253
|
+
value={editName}
|
|
254
|
+
onChange={e => setEditName(e.target.value)}
|
|
255
|
+
className="flex-1 px-3 py-1.5 rounded-md border border-edge bg-surface text-[14px] font-medium text-content"
|
|
256
|
+
placeholder="Agent name"
|
|
257
|
+
/>
|
|
258
|
+
<div className="flex items-center gap-2">
|
|
259
|
+
{selectedAgent.isDefault === 1 && (
|
|
260
|
+
<Button variant="ghost" size="sm" icon={<RotateCcw size={12} />} onClick={handleReset}>
|
|
261
|
+
Reset
|
|
262
|
+
</Button>
|
|
263
|
+
)}
|
|
264
|
+
{selectedAgent.isDefault !== 1 && (
|
|
265
|
+
<Button variant="danger" size="sm" icon={<Trash2 size={12} />} onClick={handleDelete}>
|
|
266
|
+
Delete
|
|
267
|
+
</Button>
|
|
268
|
+
)}
|
|
269
|
+
<Button variant="primary" size="sm" icon={<Save size={12} />} onClick={handleSave} disabled={saving || !hasChanges}>
|
|
270
|
+
{saving ? 'Saving...' : 'Save'}
|
|
271
|
+
</Button>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
{/* Prompt Template Editor */}
|
|
276
|
+
<div className="flex-1 flex overflow-hidden">
|
|
277
|
+
<div className="flex-1 flex flex-col p-4 overflow-hidden">
|
|
278
|
+
<div className="text-[11px] font-medium text-content-muted uppercase tracking-wider mb-2">Prompt Template</div>
|
|
279
|
+
<textarea
|
|
280
|
+
value={editPrompt}
|
|
281
|
+
onChange={e => setEditPrompt(e.target.value)}
|
|
282
|
+
className="flex-1 w-full px-3 py-2 rounded-md border border-edge bg-surface text-[12px] text-content font-mono resize-none focus:border-accent/40 focus:outline-none"
|
|
283
|
+
placeholder="Enter agent prompt template..."
|
|
284
|
+
spellCheck={false}
|
|
285
|
+
/>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
{/* Placeholder Info Panel */}
|
|
289
|
+
<div className="w-64 border-l border-edge p-3 overflow-y-auto shrink-0">
|
|
290
|
+
<div className="text-[11px] font-medium text-content-muted uppercase tracking-wider mb-3">Placeholder Variables</div>
|
|
291
|
+
<div className="space-y-2.5">
|
|
292
|
+
{PLACEHOLDER_DOCS.map(p => (
|
|
293
|
+
<div key={p.name} className="text-[11px]">
|
|
294
|
+
<code className="px-1 py-0.5 rounded bg-accent/10 text-accent font-mono text-[10px]">{p.name}</code>
|
|
295
|
+
<div className="mt-0.5 text-content-muted">{p.description}</div>
|
|
296
|
+
<div className="text-[9px] text-content-muted/60">{p.stages}</div>
|
|
297
|
+
</div>
|
|
298
|
+
))}
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
</>
|
|
303
|
+
)}
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
);
|
|
307
|
+
}
|
package/templates/assistkick-product-system/packages/frontend/src/components/CoherenceView.tsx
CHANGED
|
@@ -7,12 +7,25 @@ const TYPE_LABELS: Record<string, string> = {
|
|
|
7
7
|
deprecate_node: 'Remove Node', update_description: 'Update Description',
|
|
8
8
|
edge: 'Add Edge', node: 'Add Node',
|
|
9
9
|
};
|
|
10
|
-
const
|
|
11
|
-
add_edge: '
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const TYPE_TW: Record<string, string> = {
|
|
11
|
+
add_edge: 'text-accent bg-[rgba(77,171,247,0.12)]',
|
|
12
|
+
add_node: 'text-[#ffd43b] bg-[rgba(255,212,59,0.12)]',
|
|
13
|
+
remove_edge: 'text-[#ff6b6b] bg-[rgba(255,107,107,0.12)]',
|
|
14
|
+
deprecate_node: 'text-[#ff6b6b] bg-[rgba(255,107,107,0.12)]',
|
|
15
|
+
update_description: 'text-[#51cf66] bg-[rgba(81,207,102,0.12)]',
|
|
16
|
+
edge: 'text-accent bg-[rgba(77,171,247,0.12)]',
|
|
17
|
+
node: 'text-[#ffd43b] bg-[rgba(255,212,59,0.12)]',
|
|
18
|
+
};
|
|
19
|
+
const CONFIDENCE_TW: Record<string, string> = {
|
|
20
|
+
high: 'text-[#40c057] bg-[rgba(64,192,87,0.12)]',
|
|
21
|
+
medium: 'text-[#fab005] bg-[rgba(250,176,5,0.12)]',
|
|
22
|
+
low: 'text-content-muted bg-surface-raised',
|
|
14
23
|
};
|
|
15
24
|
const CONFIDENCE_LABELS: Record<string, string> = { high: 'High', medium: 'Medium', low: 'Low' };
|
|
25
|
+
const STATUS_TW: Record<string, string> = {
|
|
26
|
+
approved: 'text-[#40c057] bg-[rgba(64,192,87,0.12)]',
|
|
27
|
+
dismissed: 'text-content-muted bg-surface-raised',
|
|
28
|
+
};
|
|
16
29
|
|
|
17
30
|
interface CoherenceViewProps {
|
|
18
31
|
graphData: any;
|
|
@@ -161,9 +174,8 @@ export function CoherenceView({ graphData, onNodeClick, projectId }: CoherenceVi
|
|
|
161
174
|
const node = findNode(nodeId);
|
|
162
175
|
return (
|
|
163
176
|
<span
|
|
164
|
-
className=
|
|
177
|
+
className={`text-accent ${node ? 'cursor-pointer' : 'cursor-default'} no-underline hover:underline`}
|
|
165
178
|
onClick={() => { if (node) onNodeClick(node); }}
|
|
166
|
-
style={{ cursor: node ? 'pointer' : 'default' }}
|
|
167
179
|
>
|
|
168
180
|
{node ? node.name : nodeId}
|
|
169
181
|
</span>
|
|
@@ -174,34 +186,34 @@ export function CoherenceView({ graphData, onNodeClick, projectId }: CoherenceVi
|
|
|
174
186
|
switch (proposal.type) {
|
|
175
187
|
case 'add_edge': case 'edge':
|
|
176
188
|
return (
|
|
177
|
-
<div className="
|
|
189
|
+
<div className="text-xs mb-2 leading-relaxed">
|
|
178
190
|
<NodeRef nodeId={proposal.from_id} />
|
|
179
|
-
<span className="
|
|
191
|
+
<span className="text-content-muted text-[11px]"> --{proposal.relation}--> </span>
|
|
180
192
|
<NodeRef nodeId={proposal.to_id} />
|
|
181
193
|
</div>
|
|
182
194
|
);
|
|
183
195
|
case 'remove_edge':
|
|
184
196
|
return (
|
|
185
|
-
<div className="
|
|
197
|
+
<div className="text-xs mb-2 leading-relaxed line-through opacity-80">
|
|
186
198
|
<NodeRef nodeId={proposal.from_id} />
|
|
187
|
-
<span className="
|
|
199
|
+
<span className="text-content-muted text-[11px]"> --{proposal.relation}--> </span>
|
|
188
200
|
<NodeRef nodeId={proposal.to_id} />
|
|
189
201
|
</div>
|
|
190
202
|
);
|
|
191
203
|
case 'add_node': case 'node':
|
|
192
204
|
return (
|
|
193
205
|
<>
|
|
194
|
-
<div className="
|
|
195
|
-
<span className="
|
|
196
|
-
<span className="
|
|
206
|
+
<div className="flex items-center gap-2 mb-2">
|
|
207
|
+
<span className="text-[10px] text-content-muted uppercase tracking-wider bg-surface-raised px-2 py-0.5 rounded-sm">{proposal.proposed_node_type}</span>
|
|
208
|
+
<span className="text-xs font-medium text-content">{proposal.proposed_node_name}</span>
|
|
197
209
|
</div>
|
|
198
210
|
{proposal.suggested_edges?.length > 0 && (
|
|
199
|
-
<div className="
|
|
200
|
-
<span className="
|
|
211
|
+
<div className="mb-2 pl-3">
|
|
212
|
+
<span className="text-[10px] text-content-muted uppercase tracking-wider">Suggested edges:</span>
|
|
201
213
|
{proposal.suggested_edges.map((edge: any, i: number) => {
|
|
202
214
|
const fromName = edge.from_id === 'NEW' ? proposal.proposed_node_name : (findNode(edge.from_id)?.name || edge.from_id);
|
|
203
215
|
const toName = edge.to_id === 'NEW' ? proposal.proposed_node_name : (findNode(edge.to_id)?.name || edge.to_id);
|
|
204
|
-
return <div key={i} className="
|
|
216
|
+
return <div key={i} className="text-[11px] text-content-secondary py-0.5">{fromName} --{edge.relation}--> {toName}</div>;
|
|
205
217
|
})}
|
|
206
218
|
</div>
|
|
207
219
|
)}
|
|
@@ -210,16 +222,16 @@ export function CoherenceView({ graphData, onNodeClick, projectId }: CoherenceVi
|
|
|
210
222
|
case 'deprecate_node':
|
|
211
223
|
return (
|
|
212
224
|
<>
|
|
213
|
-
<div className="
|
|
225
|
+
<div className="flex items-center gap-2 mb-2 line-through opacity-80">
|
|
214
226
|
<NodeRef nodeId={proposal.target_node_id} />
|
|
215
227
|
</div>
|
|
216
228
|
{proposal.affected_edges?.length > 0 && (
|
|
217
|
-
<div className="
|
|
218
|
-
<span className="
|
|
229
|
+
<div className="mb-2 pl-3">
|
|
230
|
+
<span className="text-[10px] text-content-muted uppercase tracking-wider">
|
|
219
231
|
Will also remove {proposal.affected_edges.length} edge{proposal.affected_edges.length > 1 ? 's' : ''}:
|
|
220
232
|
</span>
|
|
221
233
|
{proposal.affected_edges.map((edge: any, i: number) => (
|
|
222
|
-
<div key={i} className="
|
|
234
|
+
<div key={i} className="text-[11px] text-content-secondary py-0.5 text-[#ff6b6b]">
|
|
223
235
|
{findNode(edge.from)?.name || edge.from} --{edge.relation}--> {findNode(edge.to)?.name || edge.to}
|
|
224
236
|
</div>
|
|
225
237
|
))}
|
|
@@ -230,63 +242,67 @@ export function CoherenceView({ graphData, onNodeClick, projectId }: CoherenceVi
|
|
|
230
242
|
case 'update_description':
|
|
231
243
|
return (
|
|
232
244
|
<>
|
|
233
|
-
<div className="
|
|
245
|
+
<div className="mb-2">
|
|
234
246
|
<NodeRef nodeId={proposal.target_node_id} />
|
|
235
247
|
</div>
|
|
236
248
|
{proposal.proposed_description && (
|
|
237
|
-
<div className="
|
|
238
|
-
<span className="
|
|
239
|
-
<div className="
|
|
249
|
+
<div className="mb-2">
|
|
250
|
+
<span className="text-[10px] text-content-muted uppercase tracking-wider">Suggested description:</span>
|
|
251
|
+
<div className="text-[11px] text-content-secondary bg-surface-raised rounded px-2.5 py-2 mt-1 whitespace-pre-wrap max-h-[120px] overflow-y-auto">{proposal.proposed_description}</div>
|
|
240
252
|
</div>
|
|
241
253
|
)}
|
|
242
254
|
</>
|
|
243
255
|
);
|
|
244
256
|
default:
|
|
245
|
-
return <div
|
|
257
|
+
return <div>{JSON.stringify(proposal)}</div>;
|
|
246
258
|
}
|
|
247
259
|
};
|
|
248
260
|
|
|
249
261
|
const ProposalCard = ({ proposal, resolved, conflicts, inBatch }: { proposal: any; resolved: boolean; conflicts: any; inBatch?: boolean }) => {
|
|
250
262
|
const hasConflict = conflicts?.[proposal.id]?.length > 0;
|
|
251
|
-
const
|
|
263
|
+
const isApproved = proposal.status === 'approved';
|
|
264
|
+
const isDismissed = proposal.status === 'dismissed';
|
|
252
265
|
|
|
253
266
|
return (
|
|
254
|
-
<div
|
|
255
|
-
|
|
256
|
-
|
|
267
|
+
<div
|
|
268
|
+
className={`bg-surface-alt border border-edge rounded-md px-4 py-3.5 mb-2.5 transition-[border-color] duration-150 hover:border-content-muted${isApproved ? ' border-l-[3px] border-l-[#40c057] opacity-70' : ''}${isDismissed ? ' border-l-[3px] border-l-content-muted opacity-50' : ''}${hasConflict ? ' border-[#ff6b6b]' : ''}`}
|
|
269
|
+
data-id={proposal.id}
|
|
270
|
+
>
|
|
271
|
+
<div className="flex items-center gap-2 mb-2">
|
|
272
|
+
<span className={`text-[10px] font-semibold uppercase tracking-wide px-2 py-0.5 rounded-sm ${TYPE_TW[proposal.type] || ''}`}>
|
|
257
273
|
{TYPE_LABELS[proposal.type] || proposal.type}
|
|
258
274
|
</span>
|
|
259
275
|
{proposal.confidence && (
|
|
260
|
-
<span className={`
|
|
276
|
+
<span className={`text-[10px] font-semibold px-2 py-0.5 rounded-sm capitalize ${CONFIDENCE_TW[proposal.confidence] || ''}`}>
|
|
261
277
|
{CONFIDENCE_LABELS[proposal.confidence] || proposal.confidence}
|
|
262
278
|
</span>
|
|
263
279
|
)}
|
|
264
280
|
{resolved && (
|
|
265
|
-
<span className={`
|
|
281
|
+
<span className={`text-[10px] px-2 py-0.5 rounded-sm font-semibold capitalize ${STATUS_TW[proposal.status] || ''}`}>{proposal.status}</span>
|
|
266
282
|
)}
|
|
267
283
|
</div>
|
|
268
284
|
{hasConflict && (
|
|
269
|
-
<div className="
|
|
285
|
+
<div className="text-[11px] text-[#ff6b6b] bg-[rgba(255,107,107,0.08)] border border-[rgba(255,107,107,0.2)] rounded px-2.5 py-1.5 mb-2">Conflicts with other proposals — resolve the conflict before approving.</div>
|
|
270
286
|
)}
|
|
271
287
|
<ProposalContent proposal={proposal} />
|
|
272
|
-
{!inBatch && <div className="
|
|
288
|
+
{!inBatch && <div className="text-xs text-content-secondary leading-relaxed mb-2.5 px-2.5 py-2 bg-surface-raised rounded">{proposal.reasoning}</div>}
|
|
273
289
|
{!resolved && (
|
|
274
|
-
<div className="
|
|
290
|
+
<div className="flex gap-2">
|
|
275
291
|
<button
|
|
276
|
-
className="
|
|
292
|
+
className="px-3.5 py-[5px] rounded font-mono text-[11px] cursor-pointer transition-[background,color] duration-150 bg-transparent border border-[#40c057] text-[#40c057] hover:enabled:bg-[#40c057] hover:enabled:text-white disabled:opacity-60 disabled:cursor-not-allowed"
|
|
277
293
|
disabled={applyingIds.has(proposal.id)}
|
|
278
294
|
onClick={() => handleApprove(proposal.id)}
|
|
279
295
|
>
|
|
280
296
|
{applyingIds.has(proposal.id) ? 'Applying...' : 'Approve'}
|
|
281
297
|
</button>
|
|
282
|
-
<button className="
|
|
298
|
+
<button className="px-3.5 py-[5px] rounded font-mono text-[11px] cursor-pointer transition-[background,color] duration-150 bg-transparent border border-edge text-content-secondary hover:border-content hover:text-content" onClick={() => handleDismiss(proposal.id)}>Dismiss</button>
|
|
283
299
|
</div>
|
|
284
300
|
)}
|
|
285
301
|
</div>
|
|
286
302
|
);
|
|
287
303
|
};
|
|
288
304
|
|
|
289
|
-
if (error) return <div className="
|
|
305
|
+
if (error) return <div className="text-error text-[13px] p-6 text-center">Failed to load coherence data: {error}</div>;
|
|
290
306
|
if (!data) return <div>Loading...</div>;
|
|
291
307
|
|
|
292
308
|
const isRunning = data.review_status === 'running';
|
|
@@ -301,14 +317,14 @@ export function CoherenceView({ graphData, onNodeClick, projectId }: CoherenceVi
|
|
|
301
317
|
const { batched, unbatched } = groupByBatch(sortedPending);
|
|
302
318
|
|
|
303
319
|
return (
|
|
304
|
-
<div className="
|
|
305
|
-
<div className="
|
|
306
|
-
<div className="
|
|
307
|
-
<h2 className="
|
|
308
|
-
<span className="
|
|
320
|
+
<div className="max-w-[800px] mx-auto">
|
|
321
|
+
<div className="flex items-center justify-between mb-5 pb-4 border-b border-edge">
|
|
322
|
+
<div className="flex flex-col gap-1">
|
|
323
|
+
<h2 className="text-base font-semibold text-content">Coherence Review</h2>
|
|
324
|
+
<span className="text-[11px] text-content-muted">Last review: {lastReview}</span>
|
|
309
325
|
</div>
|
|
310
326
|
<button
|
|
311
|
-
className={`
|
|
327
|
+
className={`px-4 py-2 bg-transparent border border-accent rounded text-accent font-mono text-xs cursor-pointer transition-[background,color] duration-150 hover:enabled:bg-accent hover:enabled:text-white disabled:opacity-60 disabled:cursor-not-allowed${isRunning ? ' animate-pulse' : ''}`}
|
|
312
328
|
disabled={runDisabled}
|
|
313
329
|
title={hasPending && !isRunning ? 'Resolve all pending proposals first' : ''}
|
|
314
330
|
onClick={handleRunReview}
|
|
@@ -318,24 +334,24 @@ export function CoherenceView({ graphData, onNodeClick, projectId }: CoherenceVi
|
|
|
318
334
|
</div>
|
|
319
335
|
|
|
320
336
|
{data.partial_run && !isRunning && (
|
|
321
|
-
<div className="
|
|
337
|
+
<div className="bg-[rgba(250,176,5,0.08)] border border-[rgba(250,176,5,0.2)] rounded px-4 py-2.5 mb-4 text-xs text-[#fab005]">
|
|
322
338
|
The last review did not complete fully. Some proposals may be missing.
|
|
323
339
|
</div>
|
|
324
340
|
)}
|
|
325
341
|
|
|
326
342
|
{isRunning && (
|
|
327
|
-
<div className="
|
|
343
|
+
<div className="bg-[rgba(77,171,247,0.08)] border border-[rgba(77,171,247,0.2)] rounded px-4 py-3 mb-4 text-xs text-accent animate-pulse">
|
|
328
344
|
{data.progress?.message || 'AI agent is analyzing the graph for inconsistencies...'}
|
|
329
345
|
</div>
|
|
330
346
|
)}
|
|
331
347
|
|
|
332
|
-
<div className="
|
|
333
|
-
<h3 className="
|
|
348
|
+
<div className="mb-6">
|
|
349
|
+
<h3 className="text-[13px] font-semibold text-content mb-3 uppercase tracking-wide">
|
|
334
350
|
Pending Proposals{hasPending ? ` (${pendingProposals.length})` : ''}
|
|
335
351
|
</h3>
|
|
336
352
|
|
|
337
353
|
{!hasPending && !isRunning && (
|
|
338
|
-
<div className={`
|
|
354
|
+
<div className={`text-xs text-content-muted p-6 text-center bg-surface-alt border border-dashed border-edge rounded-md${data.last_review_timestamp && resolvedProposals.length === 0 ? ' text-[#40c057] border-[rgba(64,192,87,0.3)] bg-[rgba(64,192,87,0.06)]' : ''}`}>
|
|
339
355
|
{data.last_review_timestamp && resolvedProposals.length === 0
|
|
340
356
|
? 'No issues found. The graph is coherent.'
|
|
341
357
|
: data.last_review_timestamp
|
|
@@ -349,18 +365,18 @@ export function CoherenceView({ graphData, onNodeClick, projectId }: CoherenceVi
|
|
|
349
365
|
const typeLabels = [...new Set(batchProposals.map((p: any) => TYPE_LABELS[p.type] || p.type))].join(', ');
|
|
350
366
|
|
|
351
367
|
return (
|
|
352
|
-
<div key={batchId} className="
|
|
353
|
-
<div className="
|
|
354
|
-
<div className="
|
|
355
|
-
<span className="
|
|
356
|
-
<span className="
|
|
368
|
+
<div key={batchId} className="bg-surface-alt border border-edge rounded-md px-4 py-3.5 mb-2.5" data-batch={batchId}>
|
|
369
|
+
<div className="flex items-center justify-between mb-2">
|
|
370
|
+
<div className="flex items-center gap-2">
|
|
371
|
+
<span className="text-[10px] font-semibold text-accent bg-[rgba(77,171,247,0.12)] px-2 py-0.5 rounded-sm">Batch ({batchProposals.length})</span>
|
|
372
|
+
<span className="text-[11px] text-content-muted">{typeLabels}</span>
|
|
357
373
|
</div>
|
|
358
|
-
<div className="
|
|
359
|
-
<button className="
|
|
374
|
+
<div className="flex gap-2">
|
|
375
|
+
<button className="px-2.5 py-1 bg-transparent border border-edge rounded text-content-secondary font-mono text-[10px] cursor-pointer hover:border-content hover:text-content" onClick={() => toggleBatch(batchId)}>
|
|
360
376
|
{isExpanded ? 'Collapse' : 'Expand to cherry-pick'}
|
|
361
377
|
</button>
|
|
362
378
|
<button
|
|
363
|
-
className="
|
|
379
|
+
className="px-3.5 py-[5px] rounded font-mono text-[11px] cursor-pointer transition-[background,color] duration-150 bg-transparent border border-[#40c057] text-[#40c057] hover:enabled:bg-[#40c057] hover:enabled:text-white disabled:opacity-60 disabled:cursor-not-allowed"
|
|
364
380
|
disabled={applyingIds.has(batchId)}
|
|
365
381
|
onClick={() => handleBatchApprove(batchId)}
|
|
366
382
|
>
|
|
@@ -368,21 +384,21 @@ export function CoherenceView({ graphData, onNodeClick, projectId }: CoherenceVi
|
|
|
368
384
|
</button>
|
|
369
385
|
</div>
|
|
370
386
|
</div>
|
|
371
|
-
<div className="
|
|
387
|
+
<div className="text-xs text-content-secondary leading-relaxed mb-2.5 px-2.5 py-2 bg-surface-raised rounded">{batchProposals[0]?.reasoning || ''}</div>
|
|
372
388
|
{isExpanded ? (
|
|
373
|
-
<div className="
|
|
389
|
+
<div className="mt-2 pt-2 border-t border-edge">
|
|
374
390
|
{batchProposals.map((p: any) => (
|
|
375
391
|
<ProposalCard key={p.id} proposal={p} resolved={false} conflicts={conflicts} inBatch />
|
|
376
392
|
))}
|
|
377
393
|
</div>
|
|
378
394
|
) : (
|
|
379
|
-
<div className="
|
|
395
|
+
<div className="flex flex-col gap-1 mt-2">
|
|
380
396
|
{batchProposals.map((p: any) => (
|
|
381
|
-
<div key={p.id} className="
|
|
382
|
-
<span className={`
|
|
397
|
+
<div key={p.id} className="flex items-center gap-2 text-[11px]">
|
|
398
|
+
<span className={`text-[10px] font-semibold uppercase tracking-wide px-2 py-0.5 rounded-sm ${TYPE_TW[p.type] || ''}`}>
|
|
383
399
|
{TYPE_LABELS[p.type] || p.type}
|
|
384
400
|
</span>
|
|
385
|
-
<span className="
|
|
401
|
+
<span className="text-content-secondary">{getProposalSummary(p)}</span>
|
|
386
402
|
</div>
|
|
387
403
|
))}
|
|
388
404
|
</div>
|
|
@@ -397,12 +413,12 @@ export function CoherenceView({ graphData, onNodeClick, projectId }: CoherenceVi
|
|
|
397
413
|
</div>
|
|
398
414
|
|
|
399
415
|
{resolvedProposals.length > 0 && (
|
|
400
|
-
<div className="
|
|
401
|
-
<button className="
|
|
402
|
-
<span className="
|
|
416
|
+
<div className="mb-6">
|
|
417
|
+
<button className="flex items-center gap-1.5 w-full py-2 bg-transparent border-none text-content-secondary font-mono text-xs font-semibold cursor-pointer uppercase tracking-wide mb-2 hover:text-content" onClick={() => setResolvedExpanded(v => !v)}>
|
|
418
|
+
<span className="text-[9px]" dangerouslySetInnerHTML={{ __html: resolvedExpanded ? '▼' : '▶' }} />
|
|
403
419
|
{' '}Resolved ({resolvedProposals.length})
|
|
404
420
|
</button>
|
|
405
|
-
<div className={
|
|
421
|
+
<div className={resolvedExpanded ? '' : 'hidden'}>
|
|
406
422
|
{resolvedProposals.map((p: any) => (
|
|
407
423
|
<ProposalCard key={p.id} proposal={p} resolved={true} conflicts={{}} />
|
|
408
424
|
))}
|