crca 1.4.0__py3-none-any.whl
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.
- .github/ISSUE_TEMPLATE/bug_report.md +65 -0
- .github/ISSUE_TEMPLATE/feature_request.md +41 -0
- .github/PULL_REQUEST_TEMPLATE.md +20 -0
- .github/workflows/publish-manual.yml +61 -0
- .github/workflows/publish.yml +64 -0
- .gitignore +214 -0
- CRCA.py +4156 -0
- LICENSE +201 -0
- MANIFEST.in +43 -0
- PKG-INFO +5035 -0
- README.md +4959 -0
- __init__.py +17 -0
- branches/CRCA-Q.py +2728 -0
- branches/crca_cg/corposwarm.py +9065 -0
- branches/crca_cg/fix_rancher_docker_creds.ps1 +155 -0
- branches/crca_cg/package.json +5 -0
- branches/crca_cg/test_bolt_integration.py +446 -0
- branches/crca_cg/test_corposwarm_comprehensive.py +773 -0
- branches/crca_cg/test_new_features.py +163 -0
- branches/crca_sd/__init__.py +149 -0
- branches/crca_sd/crca_sd_core.py +770 -0
- branches/crca_sd/crca_sd_governance.py +1325 -0
- branches/crca_sd/crca_sd_mpc.py +1130 -0
- branches/crca_sd/crca_sd_realtime.py +1844 -0
- branches/crca_sd/crca_sd_tui.py +1133 -0
- crca-1.4.0.dist-info/METADATA +5035 -0
- crca-1.4.0.dist-info/RECORD +501 -0
- crca-1.4.0.dist-info/WHEEL +4 -0
- crca-1.4.0.dist-info/licenses/LICENSE +201 -0
- docs/CRCA-Q.md +2333 -0
- examples/config.yaml.example +25 -0
- examples/crca_sd_example.py +513 -0
- examples/data_broker_example.py +294 -0
- examples/logistics_corporation.py +861 -0
- examples/palantir_example.py +299 -0
- examples/policy_bench.py +934 -0
- examples/pridnestrovia-sd.py +705 -0
- examples/pridnestrovia_realtime.py +1902 -0
- prompts/__init__.py +10 -0
- prompts/default_crca.py +101 -0
- pyproject.toml +151 -0
- requirements.txt +76 -0
- schemas/__init__.py +43 -0
- schemas/mcpSchemas.py +51 -0
- schemas/policy.py +458 -0
- templates/__init__.py +38 -0
- templates/base_specialized_agent.py +195 -0
- templates/drift_detection.py +325 -0
- templates/examples/causal_agent_template.py +309 -0
- templates/examples/drag_drop_example.py +213 -0
- templates/examples/logistics_agent_template.py +207 -0
- templates/examples/trading_agent_template.py +206 -0
- templates/feature_mixins.py +253 -0
- templates/graph_management.py +442 -0
- templates/llm_integration.py +194 -0
- templates/module_registry.py +276 -0
- templates/mpc_planner.py +280 -0
- templates/policy_loop.py +1168 -0
- templates/prediction_framework.py +448 -0
- templates/statistical_methods.py +778 -0
- tests/sanity.yml +31 -0
- tests/sanity_check +406 -0
- tests/test_core.py +47 -0
- tests/test_crca_excel.py +166 -0
- tests/test_crca_sd.py +780 -0
- tests/test_data_broker.py +424 -0
- tests/test_palantir.py +349 -0
- tools/__init__.py +38 -0
- tools/actuators.py +437 -0
- tools/bolt.diy/Dockerfile +103 -0
- tools/bolt.diy/app/components/@settings/core/AvatarDropdown.tsx +175 -0
- tools/bolt.diy/app/components/@settings/core/ControlPanel.tsx +345 -0
- tools/bolt.diy/app/components/@settings/core/constants.tsx +108 -0
- tools/bolt.diy/app/components/@settings/core/types.ts +114 -0
- tools/bolt.diy/app/components/@settings/index.ts +12 -0
- tools/bolt.diy/app/components/@settings/shared/components/TabTile.tsx +151 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ConnectionForm.tsx +193 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ConnectionTestIndicator.tsx +60 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ErrorState.tsx +102 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/LoadingState.tsx +94 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/ServiceHeader.tsx +72 -0
- tools/bolt.diy/app/components/@settings/shared/service-integration/index.ts +6 -0
- tools/bolt.diy/app/components/@settings/tabs/data/DataTab.tsx +721 -0
- tools/bolt.diy/app/components/@settings/tabs/data/DataVisualization.tsx +384 -0
- tools/bolt.diy/app/components/@settings/tabs/event-logs/EventLogsTab.tsx +1013 -0
- tools/bolt.diy/app/components/@settings/tabs/features/FeaturesTab.tsx +295 -0
- tools/bolt.diy/app/components/@settings/tabs/github/GitHubTab.tsx +281 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubAuthDialog.tsx +173 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubCacheManager.tsx +367 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubConnection.tsx +233 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubErrorBoundary.tsx +105 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubProgressiveLoader.tsx +266 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubRepositoryCard.tsx +121 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubRepositorySelector.tsx +312 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubStats.tsx +291 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/GitHubUserProfile.tsx +46 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/shared/GitHubStateIndicators.tsx +264 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/shared/RepositoryCard.tsx +361 -0
- tools/bolt.diy/app/components/@settings/tabs/github/components/shared/index.ts +11 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/GitLabTab.tsx +305 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabAuthDialog.tsx +186 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabConnection.tsx +253 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/GitLabRepositorySelector.tsx +358 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/RepositoryCard.tsx +79 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/RepositoryList.tsx +142 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/StatsDisplay.tsx +91 -0
- tools/bolt.diy/app/components/@settings/tabs/gitlab/components/index.ts +4 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpServerList.tsx +99 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpServerListItem.tsx +70 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpStatusBadge.tsx +37 -0
- tools/bolt.diy/app/components/@settings/tabs/mcp/McpTab.tsx +239 -0
- tools/bolt.diy/app/components/@settings/tabs/netlify/NetlifyTab.tsx +1393 -0
- tools/bolt.diy/app/components/@settings/tabs/netlify/components/NetlifyConnection.tsx +990 -0
- tools/bolt.diy/app/components/@settings/tabs/netlify/components/index.ts +1 -0
- tools/bolt.diy/app/components/@settings/tabs/notifications/NotificationsTab.tsx +300 -0
- tools/bolt.diy/app/components/@settings/tabs/profile/ProfileTab.tsx +181 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/cloud/CloudProvidersTab.tsx +308 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/ErrorBoundary.tsx +68 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/HealthStatusBadge.tsx +64 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/LoadingSkeleton.tsx +107 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/LocalProvidersTab.tsx +556 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/ModelCard.tsx +106 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/ProviderCard.tsx +120 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/SetupGuide.tsx +671 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/StatusDashboard.tsx +91 -0
- tools/bolt.diy/app/components/@settings/tabs/providers/local/types.ts +44 -0
- tools/bolt.diy/app/components/@settings/tabs/settings/SettingsTab.tsx +215 -0
- tools/bolt.diy/app/components/@settings/tabs/supabase/SupabaseTab.tsx +1089 -0
- tools/bolt.diy/app/components/@settings/tabs/vercel/VercelTab.tsx +909 -0
- tools/bolt.diy/app/components/@settings/tabs/vercel/components/VercelConnection.tsx +368 -0
- tools/bolt.diy/app/components/@settings/tabs/vercel/components/index.ts +1 -0
- tools/bolt.diy/app/components/@settings/utils/tab-helpers.ts +54 -0
- tools/bolt.diy/app/components/chat/APIKeyManager.tsx +169 -0
- tools/bolt.diy/app/components/chat/Artifact.tsx +296 -0
- tools/bolt.diy/app/components/chat/AssistantMessage.tsx +192 -0
- tools/bolt.diy/app/components/chat/BaseChat.module.scss +47 -0
- tools/bolt.diy/app/components/chat/BaseChat.tsx +522 -0
- tools/bolt.diy/app/components/chat/Chat.client.tsx +670 -0
- tools/bolt.diy/app/components/chat/ChatAlert.tsx +108 -0
- tools/bolt.diy/app/components/chat/ChatBox.tsx +334 -0
- tools/bolt.diy/app/components/chat/CodeBlock.module.scss +10 -0
- tools/bolt.diy/app/components/chat/CodeBlock.tsx +85 -0
- tools/bolt.diy/app/components/chat/DicussMode.tsx +17 -0
- tools/bolt.diy/app/components/chat/ExamplePrompts.tsx +37 -0
- tools/bolt.diy/app/components/chat/FilePreview.tsx +38 -0
- tools/bolt.diy/app/components/chat/GitCloneButton.tsx +327 -0
- tools/bolt.diy/app/components/chat/ImportFolderButton.tsx +141 -0
- tools/bolt.diy/app/components/chat/LLMApiAlert.tsx +109 -0
- tools/bolt.diy/app/components/chat/MCPTools.tsx +129 -0
- tools/bolt.diy/app/components/chat/Markdown.module.scss +171 -0
- tools/bolt.diy/app/components/chat/Markdown.spec.ts +48 -0
- tools/bolt.diy/app/components/chat/Markdown.tsx +252 -0
- tools/bolt.diy/app/components/chat/Messages.client.tsx +102 -0
- tools/bolt.diy/app/components/chat/ModelSelector.tsx +797 -0
- tools/bolt.diy/app/components/chat/NetlifyDeploymentLink.client.tsx +51 -0
- tools/bolt.diy/app/components/chat/ProgressCompilation.tsx +110 -0
- tools/bolt.diy/app/components/chat/ScreenshotStateManager.tsx +33 -0
- tools/bolt.diy/app/components/chat/SendButton.client.tsx +39 -0
- tools/bolt.diy/app/components/chat/SpeechRecognition.tsx +28 -0
- tools/bolt.diy/app/components/chat/StarterTemplates.tsx +38 -0
- tools/bolt.diy/app/components/chat/SupabaseAlert.tsx +199 -0
- tools/bolt.diy/app/components/chat/SupabaseConnection.tsx +339 -0
- tools/bolt.diy/app/components/chat/ThoughtBox.tsx +43 -0
- tools/bolt.diy/app/components/chat/ToolInvocations.tsx +409 -0
- tools/bolt.diy/app/components/chat/UserMessage.tsx +101 -0
- tools/bolt.diy/app/components/chat/VercelDeploymentLink.client.tsx +158 -0
- tools/bolt.diy/app/components/chat/chatExportAndImport/ExportChatButton.tsx +49 -0
- tools/bolt.diy/app/components/chat/chatExportAndImport/ImportButtons.tsx +96 -0
- tools/bolt.diy/app/components/deploy/DeployAlert.tsx +197 -0
- tools/bolt.diy/app/components/deploy/DeployButton.tsx +277 -0
- tools/bolt.diy/app/components/deploy/GitHubDeploy.client.tsx +171 -0
- tools/bolt.diy/app/components/deploy/GitHubDeploymentDialog.tsx +1041 -0
- tools/bolt.diy/app/components/deploy/GitLabDeploy.client.tsx +171 -0
- tools/bolt.diy/app/components/deploy/GitLabDeploymentDialog.tsx +764 -0
- tools/bolt.diy/app/components/deploy/NetlifyDeploy.client.tsx +246 -0
- tools/bolt.diy/app/components/deploy/VercelDeploy.client.tsx +235 -0
- tools/bolt.diy/app/components/editor/codemirror/BinaryContent.tsx +7 -0
- tools/bolt.diy/app/components/editor/codemirror/CodeMirrorEditor.tsx +555 -0
- tools/bolt.diy/app/components/editor/codemirror/EnvMasking.ts +80 -0
- tools/bolt.diy/app/components/editor/codemirror/cm-theme.ts +192 -0
- tools/bolt.diy/app/components/editor/codemirror/indent.ts +68 -0
- tools/bolt.diy/app/components/editor/codemirror/languages.ts +112 -0
- tools/bolt.diy/app/components/git/GitUrlImport.client.tsx +147 -0
- tools/bolt.diy/app/components/header/Header.tsx +42 -0
- tools/bolt.diy/app/components/header/HeaderActionButtons.client.tsx +54 -0
- tools/bolt.diy/app/components/mandate/MandateSubmission.tsx +167 -0
- tools/bolt.diy/app/components/observability/DeploymentStatus.tsx +168 -0
- tools/bolt.diy/app/components/observability/EventTimeline.tsx +119 -0
- tools/bolt.diy/app/components/observability/FileDiffViewer.tsx +121 -0
- tools/bolt.diy/app/components/observability/GovernanceStatus.tsx +197 -0
- tools/bolt.diy/app/components/observability/GovernorMetrics.tsx +246 -0
- tools/bolt.diy/app/components/observability/LogStream.tsx +244 -0
- tools/bolt.diy/app/components/observability/MandateDetails.tsx +201 -0
- tools/bolt.diy/app/components/observability/ObservabilityDashboard.tsx +200 -0
- tools/bolt.diy/app/components/sidebar/HistoryItem.tsx +187 -0
- tools/bolt.diy/app/components/sidebar/Menu.client.tsx +536 -0
- tools/bolt.diy/app/components/sidebar/date-binning.ts +59 -0
- tools/bolt.diy/app/components/txt +1 -0
- tools/bolt.diy/app/components/ui/BackgroundRays/index.tsx +18 -0
- tools/bolt.diy/app/components/ui/BackgroundRays/styles.module.scss +246 -0
- tools/bolt.diy/app/components/ui/Badge.tsx +53 -0
- tools/bolt.diy/app/components/ui/BranchSelector.tsx +270 -0
- tools/bolt.diy/app/components/ui/Breadcrumbs.tsx +101 -0
- tools/bolt.diy/app/components/ui/Button.tsx +46 -0
- tools/bolt.diy/app/components/ui/Card.tsx +55 -0
- tools/bolt.diy/app/components/ui/Checkbox.tsx +32 -0
- tools/bolt.diy/app/components/ui/CloseButton.tsx +49 -0
- tools/bolt.diy/app/components/ui/CodeBlock.tsx +103 -0
- tools/bolt.diy/app/components/ui/Collapsible.tsx +9 -0
- tools/bolt.diy/app/components/ui/ColorSchemeDialog.tsx +378 -0
- tools/bolt.diy/app/components/ui/Dialog.tsx +449 -0
- tools/bolt.diy/app/components/ui/Dropdown.tsx +63 -0
- tools/bolt.diy/app/components/ui/EmptyState.tsx +154 -0
- tools/bolt.diy/app/components/ui/FileIcon.tsx +346 -0
- tools/bolt.diy/app/components/ui/FilterChip.tsx +92 -0
- tools/bolt.diy/app/components/ui/GlowingEffect.tsx +192 -0
- tools/bolt.diy/app/components/ui/GradientCard.tsx +100 -0
- tools/bolt.diy/app/components/ui/IconButton.tsx +84 -0
- tools/bolt.diy/app/components/ui/Input.tsx +22 -0
- tools/bolt.diy/app/components/ui/Label.tsx +20 -0
- tools/bolt.diy/app/components/ui/LoadingDots.tsx +27 -0
- tools/bolt.diy/app/components/ui/LoadingOverlay.tsx +32 -0
- tools/bolt.diy/app/components/ui/PanelHeader.tsx +20 -0
- tools/bolt.diy/app/components/ui/PanelHeaderButton.tsx +36 -0
- tools/bolt.diy/app/components/ui/Popover.tsx +29 -0
- tools/bolt.diy/app/components/ui/Progress.tsx +22 -0
- tools/bolt.diy/app/components/ui/RepositoryStats.tsx +87 -0
- tools/bolt.diy/app/components/ui/ScrollArea.tsx +41 -0
- tools/bolt.diy/app/components/ui/SearchInput.tsx +80 -0
- tools/bolt.diy/app/components/ui/SearchResultItem.tsx +134 -0
- tools/bolt.diy/app/components/ui/Separator.tsx +22 -0
- tools/bolt.diy/app/components/ui/SettingsButton.tsx +35 -0
- tools/bolt.diy/app/components/ui/Slider.tsx +73 -0
- tools/bolt.diy/app/components/ui/StatusIndicator.tsx +90 -0
- tools/bolt.diy/app/components/ui/Switch.tsx +37 -0
- tools/bolt.diy/app/components/ui/Tabs.tsx +52 -0
- tools/bolt.diy/app/components/ui/TabsWithSlider.tsx +112 -0
- tools/bolt.diy/app/components/ui/ThemeSwitch.tsx +29 -0
- tools/bolt.diy/app/components/ui/Tooltip.tsx +122 -0
- tools/bolt.diy/app/components/ui/index.ts +38 -0
- tools/bolt.diy/app/components/ui/use-toast.ts +66 -0
- tools/bolt.diy/app/components/workbench/DiffView.tsx +796 -0
- tools/bolt.diy/app/components/workbench/EditorPanel.tsx +174 -0
- tools/bolt.diy/app/components/workbench/ExpoQrModal.tsx +55 -0
- tools/bolt.diy/app/components/workbench/FileBreadcrumb.tsx +150 -0
- tools/bolt.diy/app/components/workbench/FileTree.tsx +565 -0
- tools/bolt.diy/app/components/workbench/Inspector.tsx +126 -0
- tools/bolt.diy/app/components/workbench/InspectorPanel.tsx +146 -0
- tools/bolt.diy/app/components/workbench/LockManager.tsx +262 -0
- tools/bolt.diy/app/components/workbench/PortDropdown.tsx +91 -0
- tools/bolt.diy/app/components/workbench/Preview.tsx +1049 -0
- tools/bolt.diy/app/components/workbench/ScreenshotSelector.tsx +293 -0
- tools/bolt.diy/app/components/workbench/Search.tsx +257 -0
- tools/bolt.diy/app/components/workbench/Workbench.client.tsx +506 -0
- tools/bolt.diy/app/components/workbench/terminal/Terminal.tsx +131 -0
- tools/bolt.diy/app/components/workbench/terminal/TerminalManager.tsx +68 -0
- tools/bolt.diy/app/components/workbench/terminal/TerminalTabs.tsx +277 -0
- tools/bolt.diy/app/components/workbench/terminal/theme.ts +36 -0
- tools/bolt.diy/app/components/workflow/WorkflowPhase.tsx +109 -0
- tools/bolt.diy/app/components/workflow/WorkflowStatus.tsx +60 -0
- tools/bolt.diy/app/components/workflow/WorkflowTimeline.tsx +150 -0
- tools/bolt.diy/app/entry.client.tsx +7 -0
- tools/bolt.diy/app/entry.server.tsx +80 -0
- tools/bolt.diy/app/root.tsx +156 -0
- tools/bolt.diy/app/routes/_index.tsx +175 -0
- tools/bolt.diy/app/routes/api.bug-report.ts +254 -0
- tools/bolt.diy/app/routes/api.chat.ts +463 -0
- tools/bolt.diy/app/routes/api.check-env-key.ts +41 -0
- tools/bolt.diy/app/routes/api.configured-providers.ts +110 -0
- tools/bolt.diy/app/routes/api.corporate-swarm-status.ts +55 -0
- tools/bolt.diy/app/routes/api.enhancer.ts +137 -0
- tools/bolt.diy/app/routes/api.export-api-keys.ts +44 -0
- tools/bolt.diy/app/routes/api.git-info.ts +69 -0
- tools/bolt.diy/app/routes/api.git-proxy.$.ts +178 -0
- tools/bolt.diy/app/routes/api.github-branches.ts +166 -0
- tools/bolt.diy/app/routes/api.github-deploy.ts +67 -0
- tools/bolt.diy/app/routes/api.github-stats.ts +198 -0
- tools/bolt.diy/app/routes/api.github-template.ts +242 -0
- tools/bolt.diy/app/routes/api.github-user.ts +287 -0
- tools/bolt.diy/app/routes/api.gitlab-branches.ts +143 -0
- tools/bolt.diy/app/routes/api.gitlab-deploy.ts +67 -0
- tools/bolt.diy/app/routes/api.gitlab-projects.ts +105 -0
- tools/bolt.diy/app/routes/api.health.ts +8 -0
- tools/bolt.diy/app/routes/api.llmcall.ts +298 -0
- tools/bolt.diy/app/routes/api.mandate.ts +351 -0
- tools/bolt.diy/app/routes/api.mcp-check.ts +16 -0
- tools/bolt.diy/app/routes/api.mcp-update-config.ts +23 -0
- tools/bolt.diy/app/routes/api.models.$provider.ts +2 -0
- tools/bolt.diy/app/routes/api.models.ts +90 -0
- tools/bolt.diy/app/routes/api.netlify-deploy.ts +240 -0
- tools/bolt.diy/app/routes/api.netlify-user.ts +142 -0
- tools/bolt.diy/app/routes/api.supabase-user.ts +199 -0
- tools/bolt.diy/app/routes/api.supabase.query.ts +92 -0
- tools/bolt.diy/app/routes/api.supabase.ts +56 -0
- tools/bolt.diy/app/routes/api.supabase.variables.ts +32 -0
- tools/bolt.diy/app/routes/api.system.diagnostics.ts +142 -0
- tools/bolt.diy/app/routes/api.system.disk-info.ts +311 -0
- tools/bolt.diy/app/routes/api.system.git-info.ts +332 -0
- tools/bolt.diy/app/routes/api.update.ts +21 -0
- tools/bolt.diy/app/routes/api.vercel-deploy.ts +497 -0
- tools/bolt.diy/app/routes/api.vercel-user.ts +161 -0
- tools/bolt.diy/app/routes/api.workflow-status.$proposalId.ts +309 -0
- tools/bolt.diy/app/routes/chat.$id.tsx +8 -0
- tools/bolt.diy/app/routes/execute.$mandateId.tsx +432 -0
- tools/bolt.diy/app/routes/git.tsx +25 -0
- tools/bolt.diy/app/routes/observability.$mandateId.tsx +50 -0
- tools/bolt.diy/app/routes/webcontainer.connect.$id.tsx +32 -0
- tools/bolt.diy/app/routes/webcontainer.preview.$id.tsx +97 -0
- tools/bolt.diy/app/routes/workflow.$proposalId.tsx +170 -0
- tools/bolt.diy/app/styles/animations.scss +49 -0
- tools/bolt.diy/app/styles/components/code.scss +9 -0
- tools/bolt.diy/app/styles/components/editor.scss +135 -0
- tools/bolt.diy/app/styles/components/resize-handle.scss +30 -0
- tools/bolt.diy/app/styles/components/terminal.scss +3 -0
- tools/bolt.diy/app/styles/components/toast.scss +23 -0
- tools/bolt.diy/app/styles/diff-view.css +72 -0
- tools/bolt.diy/app/styles/index.scss +73 -0
- tools/bolt.diy/app/styles/variables.scss +255 -0
- tools/bolt.diy/app/styles/z-index.scss +37 -0
- tools/bolt.diy/app/types/GitHub.ts +182 -0
- tools/bolt.diy/app/types/GitLab.ts +103 -0
- tools/bolt.diy/app/types/actions.ts +85 -0
- tools/bolt.diy/app/types/artifact.ts +5 -0
- tools/bolt.diy/app/types/context.ts +26 -0
- tools/bolt.diy/app/types/design-scheme.ts +93 -0
- tools/bolt.diy/app/types/global.d.ts +13 -0
- tools/bolt.diy/app/types/mandate.ts +333 -0
- tools/bolt.diy/app/types/model.ts +25 -0
- tools/bolt.diy/app/types/netlify.ts +94 -0
- tools/bolt.diy/app/types/supabase.ts +54 -0
- tools/bolt.diy/app/types/template.ts +8 -0
- tools/bolt.diy/app/types/terminal.ts +9 -0
- tools/bolt.diy/app/types/theme.ts +1 -0
- tools/bolt.diy/app/types/vercel.ts +67 -0
- tools/bolt.diy/app/utils/buffer.ts +29 -0
- tools/bolt.diy/app/utils/classNames.ts +65 -0
- tools/bolt.diy/app/utils/constants.ts +147 -0
- tools/bolt.diy/app/utils/debounce.ts +13 -0
- tools/bolt.diy/app/utils/debugLogger.ts +1284 -0
- tools/bolt.diy/app/utils/diff.spec.ts +11 -0
- tools/bolt.diy/app/utils/diff.ts +117 -0
- tools/bolt.diy/app/utils/easings.ts +3 -0
- tools/bolt.diy/app/utils/fileLocks.ts +96 -0
- tools/bolt.diy/app/utils/fileUtils.ts +121 -0
- tools/bolt.diy/app/utils/folderImport.ts +73 -0
- tools/bolt.diy/app/utils/formatSize.ts +12 -0
- tools/bolt.diy/app/utils/getLanguageFromExtension.ts +24 -0
- tools/bolt.diy/app/utils/githubStats.ts +9 -0
- tools/bolt.diy/app/utils/gitlabStats.ts +54 -0
- tools/bolt.diy/app/utils/logger.ts +162 -0
- tools/bolt.diy/app/utils/markdown.ts +155 -0
- tools/bolt.diy/app/utils/mobile.ts +4 -0
- tools/bolt.diy/app/utils/os.ts +4 -0
- tools/bolt.diy/app/utils/path.ts +19 -0
- tools/bolt.diy/app/utils/projectCommands.ts +197 -0
- tools/bolt.diy/app/utils/promises.ts +19 -0
- tools/bolt.diy/app/utils/react.ts +6 -0
- tools/bolt.diy/app/utils/sampler.ts +49 -0
- tools/bolt.diy/app/utils/selectStarterTemplate.ts +255 -0
- tools/bolt.diy/app/utils/shell.ts +384 -0
- tools/bolt.diy/app/utils/stacktrace.ts +27 -0
- tools/bolt.diy/app/utils/stripIndent.ts +23 -0
- tools/bolt.diy/app/utils/terminal.ts +11 -0
- tools/bolt.diy/app/utils/unreachable.ts +3 -0
- tools/bolt.diy/app/vite-env.d.ts +2 -0
- tools/bolt.diy/assets/entitlements.mac.plist +25 -0
- tools/bolt.diy/assets/icons/icon.icns +0 -0
- tools/bolt.diy/assets/icons/icon.ico +0 -0
- tools/bolt.diy/assets/icons/icon.png +0 -0
- tools/bolt.diy/bindings.js +78 -0
- tools/bolt.diy/bindings.sh +33 -0
- tools/bolt.diy/docker-compose.yaml +145 -0
- tools/bolt.diy/electron/main/index.ts +201 -0
- tools/bolt.diy/electron/main/tsconfig.json +30 -0
- tools/bolt.diy/electron/main/ui/menu.ts +29 -0
- tools/bolt.diy/electron/main/ui/window.ts +54 -0
- tools/bolt.diy/electron/main/utils/auto-update.ts +110 -0
- tools/bolt.diy/electron/main/utils/constants.ts +4 -0
- tools/bolt.diy/electron/main/utils/cookie.ts +40 -0
- tools/bolt.diy/electron/main/utils/reload.ts +35 -0
- tools/bolt.diy/electron/main/utils/serve.ts +71 -0
- tools/bolt.diy/electron/main/utils/store.ts +3 -0
- tools/bolt.diy/electron/main/utils/vite-server.ts +44 -0
- tools/bolt.diy/electron/main/vite.config.ts +44 -0
- tools/bolt.diy/electron/preload/index.ts +22 -0
- tools/bolt.diy/electron/preload/tsconfig.json +7 -0
- tools/bolt.diy/electron/preload/vite.config.ts +31 -0
- tools/bolt.diy/electron-builder.yml +64 -0
- tools/bolt.diy/electron-update.yml +4 -0
- tools/bolt.diy/eslint.config.mjs +57 -0
- tools/bolt.diy/functions/[[path]].ts +12 -0
- tools/bolt.diy/icons/angular.svg +1 -0
- tools/bolt.diy/icons/astro.svg +8 -0
- tools/bolt.diy/icons/chat.svg +1 -0
- tools/bolt.diy/icons/expo-brand.svg +1 -0
- tools/bolt.diy/icons/expo.svg +4 -0
- tools/bolt.diy/icons/logo-text.svg +1 -0
- tools/bolt.diy/icons/logo.svg +4 -0
- tools/bolt.diy/icons/mcp.svg +1 -0
- tools/bolt.diy/icons/nativescript.svg +1 -0
- tools/bolt.diy/icons/netlify.svg +10 -0
- tools/bolt.diy/icons/nextjs.svg +1 -0
- tools/bolt.diy/icons/nuxt.svg +1 -0
- tools/bolt.diy/icons/qwik.svg +1 -0
- tools/bolt.diy/icons/react.svg +1 -0
- tools/bolt.diy/icons/remix.svg +24 -0
- tools/bolt.diy/icons/remotion.svg +1 -0
- tools/bolt.diy/icons/shadcn.svg +21 -0
- tools/bolt.diy/icons/slidev.svg +60 -0
- tools/bolt.diy/icons/solidjs.svg +1 -0
- tools/bolt.diy/icons/stars.svg +1 -0
- tools/bolt.diy/icons/svelte.svg +1 -0
- tools/bolt.diy/icons/typescript.svg +1 -0
- tools/bolt.diy/icons/vite.svg +1 -0
- tools/bolt.diy/icons/vue.svg +1 -0
- tools/bolt.diy/load-context.ts +9 -0
- tools/bolt.diy/notarize.cjs +31 -0
- tools/bolt.diy/package.json +218 -0
- tools/bolt.diy/playwright.config.preview.ts +35 -0
- tools/bolt.diy/pre-start.cjs +26 -0
- tools/bolt.diy/public/apple-touch-icon-precomposed.png +0 -0
- tools/bolt.diy/public/apple-touch-icon.png +0 -0
- tools/bolt.diy/public/favicon.ico +0 -0
- tools/bolt.diy/public/favicon.svg +4 -0
- tools/bolt.diy/public/icons/AmazonBedrock.svg +1 -0
- tools/bolt.diy/public/icons/Anthropic.svg +4 -0
- tools/bolt.diy/public/icons/Cohere.svg +4 -0
- tools/bolt.diy/public/icons/Deepseek.svg +5 -0
- tools/bolt.diy/public/icons/Default.svg +4 -0
- tools/bolt.diy/public/icons/Google.svg +4 -0
- tools/bolt.diy/public/icons/Groq.svg +4 -0
- tools/bolt.diy/public/icons/HuggingFace.svg +4 -0
- tools/bolt.diy/public/icons/Hyperbolic.svg +3 -0
- tools/bolt.diy/public/icons/LMStudio.svg +5 -0
- tools/bolt.diy/public/icons/Mistral.svg +4 -0
- tools/bolt.diy/public/icons/Ollama.svg +4 -0
- tools/bolt.diy/public/icons/OpenAI.svg +4 -0
- tools/bolt.diy/public/icons/OpenAILike.svg +4 -0
- tools/bolt.diy/public/icons/OpenRouter.svg +4 -0
- tools/bolt.diy/public/icons/Perplexity.svg +4 -0
- tools/bolt.diy/public/icons/Together.svg +4 -0
- tools/bolt.diy/public/icons/xAI.svg +5 -0
- tools/bolt.diy/public/inspector-script.js +292 -0
- tools/bolt.diy/public/logo-dark-styled.png +0 -0
- tools/bolt.diy/public/logo-dark.png +0 -0
- tools/bolt.diy/public/logo-light-styled.png +0 -0
- tools/bolt.diy/public/logo-light.png +0 -0
- tools/bolt.diy/public/logo.svg +15 -0
- tools/bolt.diy/public/social_preview_index.jpg +0 -0
- tools/bolt.diy/scripts/clean.js +45 -0
- tools/bolt.diy/scripts/electron-dev.mjs +181 -0
- tools/bolt.diy/scripts/setup-env.sh +41 -0
- tools/bolt.diy/scripts/update-imports.sh +7 -0
- tools/bolt.diy/scripts/update.sh +52 -0
- tools/bolt.diy/services/execution-governor/Dockerfile +41 -0
- tools/bolt.diy/services/execution-governor/config.ts +42 -0
- tools/bolt.diy/services/execution-governor/index.ts +683 -0
- tools/bolt.diy/services/execution-governor/metrics.ts +141 -0
- tools/bolt.diy/services/execution-governor/package.json +31 -0
- tools/bolt.diy/services/execution-governor/priority-queue.ts +139 -0
- tools/bolt.diy/services/execution-governor/tsconfig.json +21 -0
- tools/bolt.diy/services/execution-governor/types.ts +145 -0
- tools/bolt.diy/services/headless-executor/Dockerfile +43 -0
- tools/bolt.diy/services/headless-executor/executor.ts +210 -0
- tools/bolt.diy/services/headless-executor/index.ts +323 -0
- tools/bolt.diy/services/headless-executor/package.json +27 -0
- tools/bolt.diy/services/headless-executor/tsconfig.json +21 -0
- tools/bolt.diy/services/headless-executor/types.ts +38 -0
- tools/bolt.diy/test-workflows.sh +240 -0
- tools/bolt.diy/tests/integration/corporate-swarm.test.ts +208 -0
- tools/bolt.diy/tests/mandates/budget-limited.json +34 -0
- tools/bolt.diy/tests/mandates/complex.json +53 -0
- tools/bolt.diy/tests/mandates/constraint-enforced.json +36 -0
- tools/bolt.diy/tests/mandates/simple.json +35 -0
- tools/bolt.diy/tsconfig.json +37 -0
- tools/bolt.diy/types/istextorbinary.d.ts +15 -0
- tools/bolt.diy/uno.config.ts +279 -0
- tools/bolt.diy/vite-electron.config.ts +76 -0
- tools/bolt.diy/vite.config.ts +112 -0
- tools/bolt.diy/worker-configuration.d.ts +22 -0
- tools/bolt.diy/wrangler.toml +6 -0
- tools/code_generator.py +461 -0
- tools/file_operations.py +465 -0
- tools/mandate_generator.py +337 -0
- tools/mcpClientUtils.py +1216 -0
- tools/sensors.py +285 -0
- utils/Agent_types.py +15 -0
- utils/AnyToStr.py +0 -0
- utils/HHCS.py +277 -0
- utils/__init__.py +30 -0
- utils/agent.py +3627 -0
- utils/aop.py +2948 -0
- utils/canonical.py +143 -0
- utils/conversation.py +1195 -0
- utils/doctrine_versioning +230 -0
- utils/formatter.py +474 -0
- utils/ledger.py +311 -0
- utils/out_types.py +16 -0
- utils/rollback.py +339 -0
- utils/router.py +929 -0
- utils/tui.py +1908 -0
utils/tui.py
ADDED
|
@@ -0,0 +1,1908 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CorporateSwarm TUI - Comprehensive Interactive Terminal User Interface
|
|
3
|
+
|
|
4
|
+
A rich, fully interactive terminal interface for monitoring and controlling
|
|
5
|
+
CorporateSwarm governance systems in real-time.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Live updating dashboard
|
|
9
|
+
- Interactive member management
|
|
10
|
+
- Proposal creation and voting
|
|
11
|
+
- Board meeting scheduling
|
|
12
|
+
- ESG score visualization
|
|
13
|
+
- Risk assessment management
|
|
14
|
+
- AOP integration control
|
|
15
|
+
- Real-time metrics
|
|
16
|
+
- Full keyboard navigation and interaction
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import time
|
|
20
|
+
import threading
|
|
21
|
+
import sys
|
|
22
|
+
import select
|
|
23
|
+
import termios
|
|
24
|
+
import tty
|
|
25
|
+
from typing import Any, Dict, List, Optional, Callable, Tuple
|
|
26
|
+
from dataclasses import dataclass, field
|
|
27
|
+
from datetime import datetime
|
|
28
|
+
from enum import Enum
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
from rich.console import Console, Group
|
|
32
|
+
from rich.layout import Layout
|
|
33
|
+
from rich.panel import Panel
|
|
34
|
+
from rich.table import Table
|
|
35
|
+
from rich.text import Text
|
|
36
|
+
from rich.live import Live
|
|
37
|
+
from rich.progress import (
|
|
38
|
+
Progress, SpinnerColumn, TextColumn, BarColumn,
|
|
39
|
+
TimeElapsedColumn, ProgressColumn
|
|
40
|
+
)
|
|
41
|
+
from rich.columns import Columns
|
|
42
|
+
from rich.align import Align
|
|
43
|
+
from rich import box
|
|
44
|
+
from rich.markdown import Markdown
|
|
45
|
+
from rich.tree import Tree
|
|
46
|
+
from rich.rule import Rule
|
|
47
|
+
from rich.spinner import Spinner
|
|
48
|
+
from rich.status import Status
|
|
49
|
+
from rich.prompt import Prompt, Confirm
|
|
50
|
+
from rich.console import Group as RichGroup
|
|
51
|
+
RICH_AVAILABLE = True
|
|
52
|
+
except ImportError:
|
|
53
|
+
RICH_AVAILABLE = False
|
|
54
|
+
Console = None
|
|
55
|
+
Layout = None
|
|
56
|
+
Panel = None
|
|
57
|
+
Table = None
|
|
58
|
+
Text = None
|
|
59
|
+
Live = None
|
|
60
|
+
Progress = None
|
|
61
|
+
Columns = None
|
|
62
|
+
Align = None
|
|
63
|
+
box = None
|
|
64
|
+
Markdown = None
|
|
65
|
+
Tree = None
|
|
66
|
+
Rule = None
|
|
67
|
+
Spinner = None
|
|
68
|
+
Status = None
|
|
69
|
+
Prompt = None
|
|
70
|
+
Confirm = None
|
|
71
|
+
RichGroup = None
|
|
72
|
+
|
|
73
|
+
from loguru import logger
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ViewMode(str, Enum):
|
|
77
|
+
"""TUI view modes."""
|
|
78
|
+
DASHBOARD = "dashboard"
|
|
79
|
+
MEMBERS = "members"
|
|
80
|
+
PROPOSALS = "proposals"
|
|
81
|
+
VOTES = "votes"
|
|
82
|
+
MEETINGS = "meetings"
|
|
83
|
+
ESG = "esg"
|
|
84
|
+
RISK = "risk"
|
|
85
|
+
AOP = "aop"
|
|
86
|
+
COMMITTEES = "committees"
|
|
87
|
+
DEPARTMENTS = "departments"
|
|
88
|
+
COMMAND = "command"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class InteractionMode(str, Enum):
|
|
92
|
+
"""Interaction modes for the TUI."""
|
|
93
|
+
VIEW = "view"
|
|
94
|
+
SELECT = "select"
|
|
95
|
+
CREATE = "create"
|
|
96
|
+
EDIT = "edit"
|
|
97
|
+
DELETE = "delete"
|
|
98
|
+
COMMAND = "command"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class TUIState:
|
|
103
|
+
"""State for CorporateSwarm TUI display."""
|
|
104
|
+
corporate_swarm: Optional[Any] = None
|
|
105
|
+
view_mode: ViewMode = ViewMode.DASHBOARD
|
|
106
|
+
interaction_mode: InteractionMode = InteractionMode.VIEW
|
|
107
|
+
selected_member_id: Optional[str] = None
|
|
108
|
+
selected_proposal_id: Optional[str] = None
|
|
109
|
+
selected_committee_id: Optional[str] = None
|
|
110
|
+
selected_vote_id: Optional[str] = None
|
|
111
|
+
selected_meeting_id: Optional[str] = None
|
|
112
|
+
selected_index: int = 0
|
|
113
|
+
list_items: List[Any] = field(default_factory=list)
|
|
114
|
+
auto_refresh: bool = True
|
|
115
|
+
refresh_rate: float = 1.0
|
|
116
|
+
last_update: float = field(default_factory=time.time)
|
|
117
|
+
status_data: Optional[Dict[str, Any]] = None
|
|
118
|
+
alerts: List[Dict[str, Any]] = field(default_factory=list)
|
|
119
|
+
command_history: List[str] = field(default_factory=list)
|
|
120
|
+
current_command: str = ""
|
|
121
|
+
message: Optional[str] = None
|
|
122
|
+
message_style: str = "white"
|
|
123
|
+
|
|
124
|
+
def __post_init__(self):
|
|
125
|
+
"""Initialize default values."""
|
|
126
|
+
if self.status_data is None:
|
|
127
|
+
self.status_data = {}
|
|
128
|
+
if self.alerts is None:
|
|
129
|
+
self.alerts = []
|
|
130
|
+
if self.list_items is None:
|
|
131
|
+
self.list_items = []
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class CorporateSwarmTUI:
|
|
135
|
+
"""
|
|
136
|
+
Comprehensive Interactive Terminal User Interface for CorporateSwarm.
|
|
137
|
+
|
|
138
|
+
Provides fully interactive interface with:
|
|
139
|
+
- Corporate overview and metrics
|
|
140
|
+
- Interactive member management
|
|
141
|
+
- Proposal creation and voting
|
|
142
|
+
- Board meeting scheduling
|
|
143
|
+
- ESG score visualization
|
|
144
|
+
- Risk assessment management
|
|
145
|
+
- AOP integration control
|
|
146
|
+
- Real-time alerts and notifications
|
|
147
|
+
- Keyboard navigation and commands
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def __init__(
|
|
151
|
+
self,
|
|
152
|
+
corporate_swarm: Any,
|
|
153
|
+
title: str = "CorporateSwarm Governance System",
|
|
154
|
+
refresh_rate: float = 1.0
|
|
155
|
+
):
|
|
156
|
+
"""
|
|
157
|
+
Initialize CorporateSwarm TUI.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
corporate_swarm: CorporateSwarm instance to monitor
|
|
161
|
+
title: Title for the TUI
|
|
162
|
+
refresh_rate: Refresh rate in seconds
|
|
163
|
+
"""
|
|
164
|
+
if not RICH_AVAILABLE:
|
|
165
|
+
raise ImportError("rich is required for TUI. Install with: pip install rich")
|
|
166
|
+
|
|
167
|
+
self.corporate_swarm = corporate_swarm
|
|
168
|
+
self.console = Console()
|
|
169
|
+
self.title = title
|
|
170
|
+
self.state = TUIState(
|
|
171
|
+
corporate_swarm=corporate_swarm,
|
|
172
|
+
refresh_rate=refresh_rate
|
|
173
|
+
)
|
|
174
|
+
self.is_running = False
|
|
175
|
+
self.start_time = time.time()
|
|
176
|
+
self._should_quit = False
|
|
177
|
+
self._key_queue = None
|
|
178
|
+
self._keyboard_thread = None
|
|
179
|
+
self._setup_key_handlers()
|
|
180
|
+
|
|
181
|
+
def _setup_key_handlers(self) -> None:
|
|
182
|
+
"""Setup keyboard handlers."""
|
|
183
|
+
self._key_handlers: Dict[str, Callable] = {
|
|
184
|
+
'q': self._quit,
|
|
185
|
+
'Q': self._quit,
|
|
186
|
+
'r': self._refresh,
|
|
187
|
+
'R': self._refresh,
|
|
188
|
+
'1': lambda: self._set_view(ViewMode.DASHBOARD),
|
|
189
|
+
'2': lambda: self._set_view(ViewMode.MEMBERS),
|
|
190
|
+
'3': lambda: self._set_view(ViewMode.PROPOSALS),
|
|
191
|
+
'4': lambda: self._set_view(ViewMode.VOTES),
|
|
192
|
+
'5': lambda: self._set_view(ViewMode.MEETINGS),
|
|
193
|
+
'6': lambda: self._set_view(ViewMode.ESG),
|
|
194
|
+
'7': lambda: self._set_view(ViewMode.RISK),
|
|
195
|
+
'8': lambda: self._set_view(ViewMode.AOP),
|
|
196
|
+
'9': lambda: self._set_view(ViewMode.COMMITTEES),
|
|
197
|
+
'0': lambda: self._set_view(ViewMode.DEPARTMENTS),
|
|
198
|
+
'a': self._toggle_auto_refresh,
|
|
199
|
+
'A': self._toggle_auto_refresh,
|
|
200
|
+
'c': self._enter_command_mode,
|
|
201
|
+
'C': self._enter_command_mode,
|
|
202
|
+
'n': self._create_new_item,
|
|
203
|
+
'N': self._create_new_item,
|
|
204
|
+
's': self._select_item,
|
|
205
|
+
'S': self._select_item,
|
|
206
|
+
'enter': self._activate_selected,
|
|
207
|
+
'\r': self._activate_selected,
|
|
208
|
+
'\n': self._activate_selected,
|
|
209
|
+
'up': self._navigate_up,
|
|
210
|
+
'down': self._navigate_down,
|
|
211
|
+
'k': self._navigate_up,
|
|
212
|
+
'j': self._navigate_down,
|
|
213
|
+
'h': self._navigate_left,
|
|
214
|
+
'l': self._navigate_right,
|
|
215
|
+
'escape': self._exit_interaction,
|
|
216
|
+
'\x1b': self._exit_interaction,
|
|
217
|
+
'?': self._show_help,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
def _quit(self) -> None:
|
|
221
|
+
"""Quit the TUI."""
|
|
222
|
+
self._should_quit = True
|
|
223
|
+
|
|
224
|
+
def _refresh(self) -> None:
|
|
225
|
+
"""Manually refresh the display."""
|
|
226
|
+
self._update_status()
|
|
227
|
+
self.state.message = "Status refreshed"
|
|
228
|
+
self.state.message_style = "green"
|
|
229
|
+
|
|
230
|
+
def _set_view(self, mode: ViewMode) -> None:
|
|
231
|
+
"""Set the current view mode."""
|
|
232
|
+
self.state.view_mode = mode
|
|
233
|
+
self.state.selected_index = 0
|
|
234
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
235
|
+
self._update_list_items()
|
|
236
|
+
|
|
237
|
+
def _toggle_auto_refresh(self) -> None:
|
|
238
|
+
"""Toggle auto-refresh mode."""
|
|
239
|
+
self.state.auto_refresh = not self.state.auto_refresh
|
|
240
|
+
status = "enabled" if self.state.auto_refresh else "disabled"
|
|
241
|
+
self.state.message = f"Auto-refresh {status}"
|
|
242
|
+
self.state.message_style = "green"
|
|
243
|
+
|
|
244
|
+
def _enter_command_mode(self) -> None:
|
|
245
|
+
"""Enter command input mode."""
|
|
246
|
+
self.state.interaction_mode = InteractionMode.COMMAND
|
|
247
|
+
self.state.current_command = ""
|
|
248
|
+
|
|
249
|
+
def _create_new_item(self) -> None:
|
|
250
|
+
"""Create a new item based on current view."""
|
|
251
|
+
if self.state.view_mode == ViewMode.PROPOSALS:
|
|
252
|
+
self._create_proposal_interactive()
|
|
253
|
+
elif self.state.view_mode == ViewMode.MEMBERS:
|
|
254
|
+
self._create_member_interactive()
|
|
255
|
+
elif self.state.view_mode == ViewMode.MEETINGS:
|
|
256
|
+
self._schedule_meeting_interactive()
|
|
257
|
+
else:
|
|
258
|
+
self.state.message = "Cannot create items in this view"
|
|
259
|
+
self.state.message_style = "yellow"
|
|
260
|
+
|
|
261
|
+
def _select_item(self) -> None:
|
|
262
|
+
"""Enter selection mode."""
|
|
263
|
+
if self.state.list_items:
|
|
264
|
+
self.state.interaction_mode = InteractionMode.SELECT
|
|
265
|
+
self.state.message = "Use arrow keys to navigate, Enter to select"
|
|
266
|
+
self.state.message_style = "cyan"
|
|
267
|
+
else:
|
|
268
|
+
self.state.message = "No items to select"
|
|
269
|
+
self.state.message_style = "yellow"
|
|
270
|
+
|
|
271
|
+
def _activate_selected(self) -> None:
|
|
272
|
+
"""Activate the selected item."""
|
|
273
|
+
if self.state.interaction_mode == InteractionMode.SELECT:
|
|
274
|
+
self._show_item_details()
|
|
275
|
+
elif self.state.interaction_mode == InteractionMode.COMMAND:
|
|
276
|
+
self._execute_command()
|
|
277
|
+
|
|
278
|
+
def _navigate_up(self) -> None:
|
|
279
|
+
"""Navigate up in list."""
|
|
280
|
+
if self.state.list_items and self.state.selected_index > 0:
|
|
281
|
+
self.state.selected_index -= 1
|
|
282
|
+
|
|
283
|
+
def _navigate_down(self) -> None:
|
|
284
|
+
"""Navigate down in list."""
|
|
285
|
+
if self.state.list_items and self.state.selected_index < len(self.state.list_items) - 1:
|
|
286
|
+
self.state.selected_index += 1
|
|
287
|
+
|
|
288
|
+
def _navigate_left(self) -> None:
|
|
289
|
+
"""Navigate left (back)."""
|
|
290
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
291
|
+
self.state.message = None
|
|
292
|
+
|
|
293
|
+
def _navigate_right(self) -> None:
|
|
294
|
+
"""Navigate right (forward)."""
|
|
295
|
+
if self.state.list_items:
|
|
296
|
+
self._select_item()
|
|
297
|
+
|
|
298
|
+
def _exit_interaction(self) -> None:
|
|
299
|
+
"""Exit current interaction mode."""
|
|
300
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
301
|
+
self.state.current_command = ""
|
|
302
|
+
self.state.message = None
|
|
303
|
+
|
|
304
|
+
def _show_help(self) -> None:
|
|
305
|
+
"""Show help information."""
|
|
306
|
+
self.state.message = (
|
|
307
|
+
"Help: [1-0] Views | [N]ew | [S]elect | [C]ommand | [R]efresh | "
|
|
308
|
+
"[A]uto-refresh | [Q]uit | [?] Help"
|
|
309
|
+
)
|
|
310
|
+
self.state.message_style = "cyan"
|
|
311
|
+
|
|
312
|
+
def _update_list_items(self) -> None:
|
|
313
|
+
"""Update list items based on current view."""
|
|
314
|
+
if not self.corporate_swarm:
|
|
315
|
+
self.state.list_items = []
|
|
316
|
+
return
|
|
317
|
+
|
|
318
|
+
if self.state.view_mode == ViewMode.MEMBERS:
|
|
319
|
+
self.state.list_items = list(self.corporate_swarm.members.values())
|
|
320
|
+
elif self.state.view_mode == ViewMode.PROPOSALS:
|
|
321
|
+
self.state.list_items = self.corporate_swarm.proposals
|
|
322
|
+
elif self.state.view_mode == ViewMode.VOTES:
|
|
323
|
+
self.state.list_items = self.corporate_swarm.votes
|
|
324
|
+
elif self.state.view_mode == ViewMode.MEETINGS:
|
|
325
|
+
self.state.list_items = self.corporate_swarm.board_meetings
|
|
326
|
+
elif self.state.view_mode == ViewMode.COMMITTEES:
|
|
327
|
+
self.state.list_items = list(self.corporate_swarm.board_committees.values())
|
|
328
|
+
elif self.state.view_mode == ViewMode.DEPARTMENTS:
|
|
329
|
+
self.state.list_items = list(self.corporate_swarm.departments.values())
|
|
330
|
+
else:
|
|
331
|
+
self.state.list_items = []
|
|
332
|
+
|
|
333
|
+
# Reset selection index
|
|
334
|
+
if self.state.selected_index >= len(self.state.list_items):
|
|
335
|
+
self.state.selected_index = max(0, len(self.state.list_items) - 1)
|
|
336
|
+
|
|
337
|
+
def _show_item_details(self) -> None:
|
|
338
|
+
"""Show details for selected item."""
|
|
339
|
+
if not self.state.list_items or self.state.selected_index >= len(self.state.list_items):
|
|
340
|
+
return
|
|
341
|
+
|
|
342
|
+
selected = self.state.list_items[self.state.selected_index]
|
|
343
|
+
|
|
344
|
+
if self.state.view_mode == ViewMode.MEMBERS:
|
|
345
|
+
self.state.selected_member_id = selected.member_id
|
|
346
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
347
|
+
elif self.state.view_mode == ViewMode.PROPOSALS:
|
|
348
|
+
self.state.selected_proposal_id = selected.proposal_id
|
|
349
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
350
|
+
elif self.state.view_mode == ViewMode.VOTES:
|
|
351
|
+
self.state.selected_vote_id = selected.vote_id
|
|
352
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
353
|
+
elif self.state.view_mode == ViewMode.MEETINGS:
|
|
354
|
+
self.state.selected_meeting_id = selected.meeting_id
|
|
355
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
356
|
+
elif self.state.view_mode == ViewMode.COMMITTEES:
|
|
357
|
+
self.state.selected_committee_id = selected.committee_id
|
|
358
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
359
|
+
|
|
360
|
+
def _execute_command(self) -> None:
|
|
361
|
+
"""Execute a command."""
|
|
362
|
+
if not self.state.current_command:
|
|
363
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
364
|
+
return
|
|
365
|
+
|
|
366
|
+
cmd = self.state.current_command.strip().lower()
|
|
367
|
+
parts = cmd.split()
|
|
368
|
+
|
|
369
|
+
if not parts:
|
|
370
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
command = parts[0]
|
|
374
|
+
args = parts[1:] if len(parts) > 1 else []
|
|
375
|
+
|
|
376
|
+
try:
|
|
377
|
+
if command == "vote" and args:
|
|
378
|
+
# Vote on a proposal
|
|
379
|
+
proposal_id = args[0]
|
|
380
|
+
self._conduct_vote_interactive(proposal_id)
|
|
381
|
+
elif command == "proposal" or command == "create":
|
|
382
|
+
self._create_proposal_interactive()
|
|
383
|
+
elif command == "member" or command == "add":
|
|
384
|
+
self._create_member_interactive()
|
|
385
|
+
elif command == "meeting" or command == "schedule":
|
|
386
|
+
self._schedule_meeting_interactive()
|
|
387
|
+
elif command == "risk":
|
|
388
|
+
self._conduct_risk_assessment()
|
|
389
|
+
elif command == "esg":
|
|
390
|
+
self._calculate_esg()
|
|
391
|
+
elif command == "status":
|
|
392
|
+
self._show_full_status()
|
|
393
|
+
else:
|
|
394
|
+
self.state.message = f"Unknown command: {command}. Type 'help' for commands"
|
|
395
|
+
self.state.message_style = "yellow"
|
|
396
|
+
except Exception as e:
|
|
397
|
+
self.state.message = f"Command error: {str(e)}"
|
|
398
|
+
self.state.message_style = "red"
|
|
399
|
+
finally:
|
|
400
|
+
self.state.current_command = ""
|
|
401
|
+
self.state.interaction_mode = InteractionMode.VIEW
|
|
402
|
+
|
|
403
|
+
def _create_proposal_interactive(self) -> None:
|
|
404
|
+
"""Interactively create a proposal."""
|
|
405
|
+
if not self.corporate_swarm:
|
|
406
|
+
self.state.message = "No CorporateSwarm instance"
|
|
407
|
+
self.state.message_style = "red"
|
|
408
|
+
return
|
|
409
|
+
|
|
410
|
+
try:
|
|
411
|
+
self.console.clear()
|
|
412
|
+
self.console.print(Panel("Create New Proposal", border_style="cyan", box=box.ROUNDED))
|
|
413
|
+
|
|
414
|
+
title = Prompt.ask("Proposal Title")
|
|
415
|
+
description = Prompt.ask("Description")
|
|
416
|
+
|
|
417
|
+
# Show proposal types
|
|
418
|
+
proposal_types = [
|
|
419
|
+
"strategic_initiative", "budget_allocation", "hiring_decision",
|
|
420
|
+
"product_launch", "partnership", "merger_acquisition",
|
|
421
|
+
"policy_change", "investment", "operational_change"
|
|
422
|
+
]
|
|
423
|
+
|
|
424
|
+
self.console.print("\nProposal Types:")
|
|
425
|
+
for i, ptype in enumerate(proposal_types, 1):
|
|
426
|
+
self.console.print(f" {i}. {ptype.replace('_', ' ').title()}")
|
|
427
|
+
|
|
428
|
+
type_choice = Prompt.ask("Select proposal type (number or name)", default="1")
|
|
429
|
+
|
|
430
|
+
# Parse type choice
|
|
431
|
+
try:
|
|
432
|
+
type_index = int(type_choice) - 1
|
|
433
|
+
if 0 <= type_index < len(proposal_types):
|
|
434
|
+
proposal_type_str = proposal_types[type_index]
|
|
435
|
+
else:
|
|
436
|
+
proposal_type_str = type_choice.lower().replace(" ", "_")
|
|
437
|
+
except ValueError:
|
|
438
|
+
proposal_type_str = type_choice.lower().replace(" ", "_")
|
|
439
|
+
|
|
440
|
+
# Get department
|
|
441
|
+
dept_choice = Prompt.ask(
|
|
442
|
+
"Department (finance/operations/marketing/technology/legal/hr)",
|
|
443
|
+
default="operations"
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
# Get budget
|
|
447
|
+
budget_str = Prompt.ask("Budget Impact ($)", default="0")
|
|
448
|
+
try:
|
|
449
|
+
budget = float(budget_str)
|
|
450
|
+
except ValueError:
|
|
451
|
+
budget = 0.0
|
|
452
|
+
|
|
453
|
+
# Get sponsor (use first executive or board member)
|
|
454
|
+
sponsor_id = None
|
|
455
|
+
if self.corporate_swarm.executive_team:
|
|
456
|
+
sponsor_id = self.corporate_swarm.executive_team[0]
|
|
457
|
+
elif self.corporate_swarm.board_members:
|
|
458
|
+
sponsor_id = self.corporate_swarm.board_members[0]
|
|
459
|
+
|
|
460
|
+
if not sponsor_id:
|
|
461
|
+
self.state.message = "No sponsor available"
|
|
462
|
+
self.state.message_style = "red"
|
|
463
|
+
return
|
|
464
|
+
|
|
465
|
+
# Import ProposalType
|
|
466
|
+
from crca_cg.corposwarm import ProposalType, DepartmentType
|
|
467
|
+
|
|
468
|
+
# Map strings to enums
|
|
469
|
+
proposal_type_map = {
|
|
470
|
+
"strategic_initiative": ProposalType.STRATEGIC_INITIATIVE,
|
|
471
|
+
"budget_allocation": ProposalType.BUDGET_ALLOCATION,
|
|
472
|
+
"hiring_decision": ProposalType.HIRING_DECISION,
|
|
473
|
+
"product_launch": ProposalType.PRODUCT_LAUNCH,
|
|
474
|
+
"partnership": ProposalType.PARTNERSHIP,
|
|
475
|
+
"merger_acquisition": ProposalType.MERGER_ACQUISITION,
|
|
476
|
+
"policy_change": ProposalType.POLICY_CHANGE,
|
|
477
|
+
"investment": ProposalType.INVESTMENT,
|
|
478
|
+
"operational_change": ProposalType.OPERATIONAL_CHANGE,
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
dept_map = {
|
|
482
|
+
"finance": DepartmentType.FINANCE,
|
|
483
|
+
"operations": DepartmentType.OPERATIONS,
|
|
484
|
+
"marketing": DepartmentType.MARKETING,
|
|
485
|
+
"technology": DepartmentType.TECHNOLOGY,
|
|
486
|
+
"legal": DepartmentType.LEGAL,
|
|
487
|
+
"hr": DepartmentType.HUMAN_RESOURCES,
|
|
488
|
+
"human_resources": DepartmentType.HUMAN_RESOURCES,
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
proposal_type = proposal_type_map.get(proposal_type_str, ProposalType.STRATEGIC_INITIATIVE)
|
|
492
|
+
department = dept_map.get(dept_choice.lower(), DepartmentType.OPERATIONS)
|
|
493
|
+
|
|
494
|
+
# Create proposal
|
|
495
|
+
proposal_id = self.corporate_swarm.create_proposal(
|
|
496
|
+
title=title,
|
|
497
|
+
description=description,
|
|
498
|
+
proposal_type=proposal_type,
|
|
499
|
+
sponsor_id=sponsor_id,
|
|
500
|
+
department=department,
|
|
501
|
+
budget_impact=budget
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
self.state.message = f"Proposal created: {proposal_id}"
|
|
505
|
+
self.state.message_style = "green"
|
|
506
|
+
self._update_list_items()
|
|
507
|
+
|
|
508
|
+
except KeyboardInterrupt:
|
|
509
|
+
self.state.message = "Proposal creation cancelled"
|
|
510
|
+
self.state.message_style = "yellow"
|
|
511
|
+
except Exception as e:
|
|
512
|
+
self.state.message = f"Error creating proposal: {str(e)}"
|
|
513
|
+
self.state.message_style = "red"
|
|
514
|
+
|
|
515
|
+
def _create_member_interactive(self) -> None:
|
|
516
|
+
"""Interactively create a member."""
|
|
517
|
+
if not self.corporate_swarm:
|
|
518
|
+
self.state.message = "No CorporateSwarm instance"
|
|
519
|
+
self.state.message_style = "red"
|
|
520
|
+
return
|
|
521
|
+
|
|
522
|
+
try:
|
|
523
|
+
self.console.clear()
|
|
524
|
+
self.console.print(Panel("Add New Member", border_style="cyan", box=box.ROUNDED))
|
|
525
|
+
|
|
526
|
+
name = Prompt.ask("Member Name")
|
|
527
|
+
|
|
528
|
+
# Show roles
|
|
529
|
+
from crca_cg.corposwarm import CorporateRole, DepartmentType
|
|
530
|
+
|
|
531
|
+
roles = [
|
|
532
|
+
"ceo", "cfo", "cto", "coo",
|
|
533
|
+
"board_chair", "board_member", "independent_director",
|
|
534
|
+
"department_head", "manager", "employee"
|
|
535
|
+
]
|
|
536
|
+
|
|
537
|
+
self.console.print("\nRoles:")
|
|
538
|
+
for i, role in enumerate(roles, 1):
|
|
539
|
+
self.console.print(f" {i}. {role.replace('_', ' ').title()}")
|
|
540
|
+
|
|
541
|
+
role_choice = Prompt.ask("Select role (number or name)", default="employee")
|
|
542
|
+
|
|
543
|
+
# Parse role
|
|
544
|
+
try:
|
|
545
|
+
role_index = int(role_choice) - 1
|
|
546
|
+
if 0 <= role_index < len(roles):
|
|
547
|
+
role_str = roles[role_index]
|
|
548
|
+
else:
|
|
549
|
+
role_str = role_choice.lower().replace(" ", "_")
|
|
550
|
+
except ValueError:
|
|
551
|
+
role_str = role_choice.lower().replace(" ", "_")
|
|
552
|
+
|
|
553
|
+
# Get department
|
|
554
|
+
dept_choice = Prompt.ask(
|
|
555
|
+
"Department (finance/operations/marketing/technology/legal/hr)",
|
|
556
|
+
default="operations"
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
# Get expertise
|
|
560
|
+
expertise_str = Prompt.ask("Expertise areas (comma-separated)", default="")
|
|
561
|
+
expertise = [e.strip() for e in expertise_str.split(",") if e.strip()]
|
|
562
|
+
|
|
563
|
+
# Get voting weight
|
|
564
|
+
weight_str = Prompt.ask("Voting Weight", default="1.0")
|
|
565
|
+
try:
|
|
566
|
+
weight = float(weight_str)
|
|
567
|
+
except ValueError:
|
|
568
|
+
weight = 1.0
|
|
569
|
+
|
|
570
|
+
# Map to enums
|
|
571
|
+
role_map = {
|
|
572
|
+
"ceo": CorporateRole.CEO,
|
|
573
|
+
"cfo": CorporateRole.CFO,
|
|
574
|
+
"cto": CorporateRole.CTO,
|
|
575
|
+
"coo": CorporateRole.COO,
|
|
576
|
+
"board_chair": CorporateRole.BOARD_CHAIR,
|
|
577
|
+
"board_member": CorporateRole.BOARD_MEMBER,
|
|
578
|
+
"independent_director": CorporateRole.INDEPENDENT_DIRECTOR,
|
|
579
|
+
"department_head": CorporateRole.DEPARTMENT_HEAD,
|
|
580
|
+
"manager": CorporateRole.MANAGER,
|
|
581
|
+
"employee": CorporateRole.EMPLOYEE,
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
dept_map = {
|
|
585
|
+
"finance": DepartmentType.FINANCE,
|
|
586
|
+
"operations": DepartmentType.OPERATIONS,
|
|
587
|
+
"marketing": DepartmentType.MARKETING,
|
|
588
|
+
"technology": DepartmentType.TECHNOLOGY,
|
|
589
|
+
"legal": DepartmentType.LEGAL,
|
|
590
|
+
"hr": DepartmentType.HUMAN_RESOURCES,
|
|
591
|
+
"human_resources": DepartmentType.HUMAN_RESOURCES,
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
role = role_map.get(role_str, CorporateRole.EMPLOYEE)
|
|
595
|
+
department = dept_map.get(dept_choice.lower(), DepartmentType.OPERATIONS)
|
|
596
|
+
|
|
597
|
+
# Create member
|
|
598
|
+
member_id = self.corporate_swarm.add_member(
|
|
599
|
+
name=name,
|
|
600
|
+
role=role,
|
|
601
|
+
department=department,
|
|
602
|
+
expertise_areas=expertise,
|
|
603
|
+
voting_weight=weight
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
self.state.message = f"Member added: {name} ({member_id})"
|
|
607
|
+
self.state.message_style = "green"
|
|
608
|
+
self._update_list_items()
|
|
609
|
+
|
|
610
|
+
except KeyboardInterrupt:
|
|
611
|
+
self.state.message = "Member creation cancelled"
|
|
612
|
+
self.state.message_style = "yellow"
|
|
613
|
+
except Exception as e:
|
|
614
|
+
self.state.message = f"Error adding member: {str(e)}"
|
|
615
|
+
self.state.message_style = "red"
|
|
616
|
+
|
|
617
|
+
def _schedule_meeting_interactive(self) -> None:
|
|
618
|
+
"""Interactively schedule a meeting."""
|
|
619
|
+
if not self.corporate_swarm:
|
|
620
|
+
self.state.message = "No CorporateSwarm instance"
|
|
621
|
+
self.state.message_style = "red"
|
|
622
|
+
return
|
|
623
|
+
|
|
624
|
+
try:
|
|
625
|
+
self.console.clear()
|
|
626
|
+
self.console.print(Panel("Schedule Board Meeting", border_style="cyan", box=box.ROUNDED))
|
|
627
|
+
|
|
628
|
+
from crca_cg.corposwarm import MeetingType
|
|
629
|
+
|
|
630
|
+
meeting_types = [
|
|
631
|
+
"regular_board", "special_board", "annual_general",
|
|
632
|
+
"committee_meeting", "executive_session", "emergency_meeting"
|
|
633
|
+
]
|
|
634
|
+
|
|
635
|
+
self.console.print("\nMeeting Types:")
|
|
636
|
+
for i, mtype in enumerate(meeting_types, 1):
|
|
637
|
+
self.console.print(f" {i}. {mtype.replace('_', ' ').title()}")
|
|
638
|
+
|
|
639
|
+
type_choice = Prompt.ask("Select meeting type (number or name)", default="1")
|
|
640
|
+
|
|
641
|
+
try:
|
|
642
|
+
type_index = int(type_choice) - 1
|
|
643
|
+
if 0 <= type_index < len(meeting_types):
|
|
644
|
+
meeting_type_str = meeting_types[type_index]
|
|
645
|
+
else:
|
|
646
|
+
meeting_type_str = type_choice.lower().replace(" ", "_")
|
|
647
|
+
except ValueError:
|
|
648
|
+
meeting_type_str = type_choice.lower().replace(" ", "_")
|
|
649
|
+
|
|
650
|
+
meeting_type_map = {
|
|
651
|
+
"regular_board": MeetingType.REGULAR_BOARD,
|
|
652
|
+
"special_board": MeetingType.SPECIAL_BOARD,
|
|
653
|
+
"annual_general": MeetingType.ANNUAL_GENERAL,
|
|
654
|
+
"committee_meeting": MeetingType.COMMITTEE_MEETING,
|
|
655
|
+
"executive_session": MeetingType.EXECUTIVE_SESSION,
|
|
656
|
+
"emergency_meeting": MeetingType.EMERGENCY_MEETING,
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
meeting_type = meeting_type_map.get(meeting_type_str, MeetingType.REGULAR_BOARD)
|
|
660
|
+
|
|
661
|
+
# Get agenda items
|
|
662
|
+
agenda_str = Prompt.ask("Agenda items (comma-separated)", default="")
|
|
663
|
+
agenda = [a.strip() for a in agenda_str.split(",") if a.strip()]
|
|
664
|
+
|
|
665
|
+
# Schedule meeting
|
|
666
|
+
meeting_id = self.corporate_swarm.schedule_board_meeting(
|
|
667
|
+
meeting_type=meeting_type,
|
|
668
|
+
agenda=agenda if agenda else None
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
self.state.message = f"Meeting scheduled: {meeting_id}"
|
|
672
|
+
self.state.message_style = "green"
|
|
673
|
+
self._update_list_items()
|
|
674
|
+
|
|
675
|
+
except KeyboardInterrupt:
|
|
676
|
+
self.state.message = "Meeting scheduling cancelled"
|
|
677
|
+
self.state.message_style = "yellow"
|
|
678
|
+
except Exception as e:
|
|
679
|
+
self.state.message = f"Error scheduling meeting: {str(e)}"
|
|
680
|
+
self.state.message_style = "red"
|
|
681
|
+
|
|
682
|
+
def _conduct_vote_interactive(self, proposal_id: Optional[str] = None) -> None:
|
|
683
|
+
"""Interactively conduct a vote."""
|
|
684
|
+
if not self.corporate_swarm:
|
|
685
|
+
self.state.message = "No CorporateSwarm instance"
|
|
686
|
+
self.state.message_style = "red"
|
|
687
|
+
return
|
|
688
|
+
|
|
689
|
+
try:
|
|
690
|
+
if not proposal_id:
|
|
691
|
+
# Show proposals
|
|
692
|
+
if not self.corporate_swarm.proposals:
|
|
693
|
+
self.state.message = "No proposals available"
|
|
694
|
+
self.state.message_style = "yellow"
|
|
695
|
+
return
|
|
696
|
+
|
|
697
|
+
self.console.clear()
|
|
698
|
+
self.console.print(Panel("Select Proposal to Vote", border_style="cyan", box=box.ROUNDED))
|
|
699
|
+
|
|
700
|
+
for i, proposal in enumerate(self.corporate_swarm.proposals[-10:], 1):
|
|
701
|
+
self.console.print(f" {i}. {proposal.title} ({proposal.proposal_id[:8]})")
|
|
702
|
+
|
|
703
|
+
choice = Prompt.ask("Select proposal (number or ID)", default="1")
|
|
704
|
+
|
|
705
|
+
try:
|
|
706
|
+
index = int(choice) - 1
|
|
707
|
+
if 0 <= index < len(self.corporate_swarm.proposals[-10:]):
|
|
708
|
+
proposal_id = self.corporate_swarm.proposals[-10:][index].proposal_id
|
|
709
|
+
else:
|
|
710
|
+
proposal_id = choice
|
|
711
|
+
except ValueError:
|
|
712
|
+
proposal_id = choice
|
|
713
|
+
|
|
714
|
+
# Conduct vote
|
|
715
|
+
vote = self.corporate_swarm.conduct_corporate_vote(proposal_id)
|
|
716
|
+
|
|
717
|
+
self.state.message = f"Vote completed: {vote.result.value.upper()}"
|
|
718
|
+
self.state.message_style = "green"
|
|
719
|
+
self._update_list_items()
|
|
720
|
+
|
|
721
|
+
except Exception as e:
|
|
722
|
+
self.state.message = f"Error conducting vote: {str(e)}"
|
|
723
|
+
self.state.message_style = "red"
|
|
724
|
+
|
|
725
|
+
def _conduct_risk_assessment(self) -> None:
|
|
726
|
+
"""Conduct risk assessment."""
|
|
727
|
+
if not self.corporate_swarm:
|
|
728
|
+
return
|
|
729
|
+
|
|
730
|
+
try:
|
|
731
|
+
category = Prompt.ask("Risk category (or 'comprehensive')", default="comprehensive")
|
|
732
|
+
assessments = self.corporate_swarm.conduct_risk_assessment(category)
|
|
733
|
+
self.state.message = f"Risk assessment completed: {len(assessments)} categories"
|
|
734
|
+
self.state.message_style = "green"
|
|
735
|
+
self._update_status()
|
|
736
|
+
except Exception as e:
|
|
737
|
+
self.state.message = f"Error: {str(e)}"
|
|
738
|
+
self.state.message_style = "red"
|
|
739
|
+
|
|
740
|
+
def _calculate_esg(self) -> None:
|
|
741
|
+
"""Calculate ESG score."""
|
|
742
|
+
if not self.corporate_swarm:
|
|
743
|
+
return
|
|
744
|
+
|
|
745
|
+
try:
|
|
746
|
+
esg_score = self.corporate_swarm.calculate_esg_score()
|
|
747
|
+
self.state.message = f"ESG Score: {esg_score.overall_score:.1f}%"
|
|
748
|
+
self.state.message_style = "green"
|
|
749
|
+
self._update_status()
|
|
750
|
+
except Exception as e:
|
|
751
|
+
self.state.message = f"Error: {str(e)}"
|
|
752
|
+
self.state.message_style = "red"
|
|
753
|
+
|
|
754
|
+
def _show_full_status(self) -> None:
|
|
755
|
+
"""Show full corporate status."""
|
|
756
|
+
self._update_status()
|
|
757
|
+
self.state.view_mode = ViewMode.DASHBOARD
|
|
758
|
+
self.state.message = "Full status displayed"
|
|
759
|
+
self.state.message_style = "green"
|
|
760
|
+
|
|
761
|
+
def _update_status(self) -> None:
|
|
762
|
+
"""Update status data from CorporateSwarm."""
|
|
763
|
+
try:
|
|
764
|
+
if self.corporate_swarm:
|
|
765
|
+
self.state.status_data = self.corporate_swarm.get_corporate_status()
|
|
766
|
+
self.state.last_update = time.time()
|
|
767
|
+
except Exception as e:
|
|
768
|
+
logger.error(f"Error updating status: {e}")
|
|
769
|
+
|
|
770
|
+
def create_layout(self) -> Layout:
|
|
771
|
+
"""Create the main layout structure."""
|
|
772
|
+
layout = Layout()
|
|
773
|
+
|
|
774
|
+
layout.split_column(
|
|
775
|
+
Layout(name="header", size=3),
|
|
776
|
+
Layout(name="main", ratio=1),
|
|
777
|
+
Layout(name="footer", size=4)
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
layout["main"].split_row(
|
|
781
|
+
Layout(name="left", ratio=1),
|
|
782
|
+
Layout(name="right", ratio=1)
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
return layout
|
|
786
|
+
|
|
787
|
+
def render_header(self) -> Panel:
|
|
788
|
+
"""Render the header panel."""
|
|
789
|
+
uptime = time.time() - self.start_time
|
|
790
|
+
uptime_str = f"{int(uptime // 3600)}h {int((uptime % 3600) // 60)}m {int(uptime % 60)}s"
|
|
791
|
+
|
|
792
|
+
last_update_str = datetime.fromtimestamp(self.state.last_update).strftime("%H:%M:%S")
|
|
793
|
+
|
|
794
|
+
header_text = Text()
|
|
795
|
+
header_text.append(self.title, style="bold cyan")
|
|
796
|
+
header_text.append(" | ", style="dim")
|
|
797
|
+
header_text.append(f"Uptime: {uptime_str}", style="green")
|
|
798
|
+
header_text.append(" | ", style="dim")
|
|
799
|
+
header_text.append(f"Last Update: {last_update_str}", style="yellow")
|
|
800
|
+
header_text.append(" | ", style="dim")
|
|
801
|
+
|
|
802
|
+
if self.state.auto_refresh:
|
|
803
|
+
header_text.append("Auto-Refresh ON", style="green")
|
|
804
|
+
else:
|
|
805
|
+
header_text.append("Auto-Refresh OFF", style="yellow")
|
|
806
|
+
|
|
807
|
+
header_text.append(" | ", style="dim")
|
|
808
|
+
header_text.append(f"View: {self.state.view_mode.value.upper()}", style="magenta")
|
|
809
|
+
|
|
810
|
+
if self.state.interaction_mode != InteractionMode.VIEW:
|
|
811
|
+
header_text.append(" | ", style="dim")
|
|
812
|
+
header_text.append(f"Mode: {self.state.interaction_mode.value.upper()}", style="cyan")
|
|
813
|
+
|
|
814
|
+
return Panel(
|
|
815
|
+
Align.center(header_text),
|
|
816
|
+
border_style="cyan",
|
|
817
|
+
box=box.ROUNDED
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
def render_footer(self) -> Layout:
|
|
821
|
+
"""Render the footer with controls and messages."""
|
|
822
|
+
footer_layout = Layout()
|
|
823
|
+
footer_layout.split_column(
|
|
824
|
+
Layout(name="controls", size=2),
|
|
825
|
+
Layout(name="message", size=2)
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
# Controls
|
|
829
|
+
controls = Text()
|
|
830
|
+
controls.append("Controls: ", style="bold")
|
|
831
|
+
controls.append("[Q]uit ", style="red")
|
|
832
|
+
controls.append("[R]efresh ", style="yellow")
|
|
833
|
+
controls.append("[1-0] Views ", style="cyan")
|
|
834
|
+
controls.append("[N]ew ", style="green")
|
|
835
|
+
controls.append("[S]elect ", style="blue")
|
|
836
|
+
controls.append("[C]ommand ", style="magenta")
|
|
837
|
+
controls.append("[A]uto-refresh ", style="green")
|
|
838
|
+
controls.append("[?] Help", style="dim")
|
|
839
|
+
|
|
840
|
+
footer_layout["controls"].update(
|
|
841
|
+
Panel(Align.center(controls), border_style="blue", box=box.ROUNDED)
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
# Message/Command line
|
|
845
|
+
if self.state.interaction_mode == InteractionMode.COMMAND:
|
|
846
|
+
cmd_text = Text()
|
|
847
|
+
cmd_text.append("Command: ", style="bold cyan")
|
|
848
|
+
cmd_text.append(self.state.current_command, style="white")
|
|
849
|
+
cmd_text.append("_", style="dim") # Cursor
|
|
850
|
+
footer_layout["message"].update(
|
|
851
|
+
Panel(cmd_text, border_style="cyan", box=box.ROUNDED)
|
|
852
|
+
)
|
|
853
|
+
elif self.state.message:
|
|
854
|
+
msg_text = Text(self.state.message, style=self.state.message_style)
|
|
855
|
+
footer_layout["message"].update(
|
|
856
|
+
Panel(Align.center(msg_text), border_style=self.state.message_style, box=box.ROUNDED)
|
|
857
|
+
)
|
|
858
|
+
else:
|
|
859
|
+
footer_layout["message"].update(
|
|
860
|
+
Panel("", border_style="dim", box=box.ROUNDED)
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
return footer_layout
|
|
864
|
+
|
|
865
|
+
def render_dashboard(self) -> Layout:
|
|
866
|
+
"""Render the main dashboard view."""
|
|
867
|
+
layout = Layout()
|
|
868
|
+
layout.split_column(
|
|
869
|
+
Layout(name="overview", size=8),
|
|
870
|
+
Layout(name="metrics", ratio=1),
|
|
871
|
+
Layout(name="recent", ratio=1)
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
layout["overview"].update(self._render_overview_panel())
|
|
875
|
+
layout["metrics"].update(self._render_metrics_panel())
|
|
876
|
+
layout["recent"].update(self._render_recent_activity_panel())
|
|
877
|
+
|
|
878
|
+
return layout
|
|
879
|
+
|
|
880
|
+
def _render_overview_panel(self) -> Panel:
|
|
881
|
+
"""Render corporate overview panel."""
|
|
882
|
+
status = self.state.status_data or {}
|
|
883
|
+
|
|
884
|
+
overview_table = Table.grid(padding=(0, 2))
|
|
885
|
+
overview_table.add_column(style="cyan", justify="right")
|
|
886
|
+
overview_table.add_column(style="white")
|
|
887
|
+
|
|
888
|
+
overview_table.add_row("Corporation:", status.get("name", "N/A"))
|
|
889
|
+
overview_table.add_row("Members:", str(status.get("total_members", 0)))
|
|
890
|
+
overview_table.add_row("Board Members:", str(status.get("board_members", 0)))
|
|
891
|
+
overview_table.add_row("Executive Team:", str(status.get("executive_team", 0)))
|
|
892
|
+
overview_table.add_row("Departments:", str(status.get("departments", 0)))
|
|
893
|
+
overview_table.add_row("Committees:", str(status.get("board_committees", 0)))
|
|
894
|
+
overview_table.add_row("Active Proposals:", str(status.get("active_proposals", 0)))
|
|
895
|
+
overview_table.add_row("Total Votes:", str(status.get("total_votes", 0)))
|
|
896
|
+
|
|
897
|
+
return Panel(
|
|
898
|
+
overview_table,
|
|
899
|
+
title="Corporate Overview",
|
|
900
|
+
border_style="cyan",
|
|
901
|
+
box=box.ROUNDED
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
def _render_metrics_panel(self) -> Panel:
|
|
905
|
+
"""Render key metrics panel."""
|
|
906
|
+
status = self.state.status_data or {}
|
|
907
|
+
esg = status.get("esg_governance", {})
|
|
908
|
+
risk = status.get("risk_management", {})
|
|
909
|
+
|
|
910
|
+
metrics_table = Table.grid(padding=(0, 2))
|
|
911
|
+
metrics_table.add_column(style="yellow", justify="right")
|
|
912
|
+
metrics_table.add_column(style="white")
|
|
913
|
+
|
|
914
|
+
# ESG Scores
|
|
915
|
+
esg_score = esg.get("overall_score", 0)
|
|
916
|
+
metrics_table.add_row(
|
|
917
|
+
"ESG Score:",
|
|
918
|
+
self._colorize_score(esg_score, 70, 85)
|
|
919
|
+
)
|
|
920
|
+
|
|
921
|
+
metrics_table.add_row(
|
|
922
|
+
"Environmental:",
|
|
923
|
+
self._colorize_score(esg.get("environmental_score", 0), 70, 85)
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
metrics_table.add_row(
|
|
927
|
+
"Social:",
|
|
928
|
+
self._colorize_score(esg.get("social_score", 0), 70, 85)
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
metrics_table.add_row(
|
|
932
|
+
"Governance:",
|
|
933
|
+
self._colorize_score(esg.get("governance_score", 0), 70, 85)
|
|
934
|
+
)
|
|
935
|
+
|
|
936
|
+
metrics_table.add_row("", "") # Spacer
|
|
937
|
+
|
|
938
|
+
# Risk Metrics
|
|
939
|
+
total_risks = risk.get("total_risks", 0)
|
|
940
|
+
high_risks = risk.get("high_risks", 0)
|
|
941
|
+
medium_risks = risk.get("medium_risks", 0)
|
|
942
|
+
|
|
943
|
+
metrics_table.add_row(
|
|
944
|
+
"Total Risks:",
|
|
945
|
+
str(total_risks)
|
|
946
|
+
)
|
|
947
|
+
|
|
948
|
+
metrics_table.add_row(
|
|
949
|
+
"High Risks:",
|
|
950
|
+
f"[red]{high_risks}[/red]"
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
metrics_table.add_row(
|
|
954
|
+
"Medium Risks:",
|
|
955
|
+
f"[yellow]{medium_risks}[/yellow]"
|
|
956
|
+
)
|
|
957
|
+
|
|
958
|
+
return Panel(
|
|
959
|
+
metrics_table,
|
|
960
|
+
title="Key Metrics",
|
|
961
|
+
border_style="yellow",
|
|
962
|
+
box=box.ROUNDED
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
def _render_recent_activity_panel(self) -> Panel:
|
|
966
|
+
"""Render recent activity panel."""
|
|
967
|
+
status = self.state.status_data or {}
|
|
968
|
+
recent_decisions = status.get("recent_decisions", [])
|
|
969
|
+
|
|
970
|
+
if not recent_decisions:
|
|
971
|
+
content = Text("No recent decisions", style="dim")
|
|
972
|
+
else:
|
|
973
|
+
content = Table.grid(padding=(0, 1))
|
|
974
|
+
content.add_column(style="cyan")
|
|
975
|
+
content.add_column(style="white")
|
|
976
|
+
content.add_column(style="dim")
|
|
977
|
+
|
|
978
|
+
for decision in recent_decisions[-5:]:
|
|
979
|
+
proposal = decision.get("proposal", "Unknown")
|
|
980
|
+
result = decision.get("result", "unknown")
|
|
981
|
+
timestamp = decision.get("timestamp", 0)
|
|
982
|
+
|
|
983
|
+
time_str = datetime.fromtimestamp(timestamp).strftime("%H:%M:%S") if timestamp else "N/A"
|
|
984
|
+
|
|
985
|
+
result_style = "green" if result == "approved" else "red" if result == "rejected" else "yellow"
|
|
986
|
+
|
|
987
|
+
content.add_row(
|
|
988
|
+
proposal[:30] + ("..." if len(proposal) > 30 else ""),
|
|
989
|
+
f"[{result_style}]{result.upper()}[/{result_style}]",
|
|
990
|
+
time_str
|
|
991
|
+
)
|
|
992
|
+
|
|
993
|
+
return Panel(
|
|
994
|
+
content,
|
|
995
|
+
title="Recent Decisions",
|
|
996
|
+
border_style="green",
|
|
997
|
+
box=box.ROUNDED
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
def render_members_view(self) -> Panel:
|
|
1001
|
+
"""Render members view with selection."""
|
|
1002
|
+
if not self.corporate_swarm:
|
|
1003
|
+
return Panel("No CorporateSwarm instance", border_style="red")
|
|
1004
|
+
|
|
1005
|
+
members_table = Table(show_header=True, header_style="bold cyan", box=box.ROUNDED)
|
|
1006
|
+
members_table.add_column("", width=2) # Selection indicator
|
|
1007
|
+
members_table.add_column("Name", style="cyan", width=20)
|
|
1008
|
+
members_table.add_column("Role", style="yellow", width=15)
|
|
1009
|
+
members_table.add_column("Department", style="green", width=15)
|
|
1010
|
+
members_table.add_column("Expertise", style="white", width=25)
|
|
1011
|
+
members_table.add_column("Voting Weight", style="magenta", justify="right", width=12)
|
|
1012
|
+
|
|
1013
|
+
for idx, (member_id, member) in enumerate(self.corporate_swarm.members.items()):
|
|
1014
|
+
expertise_str = ", ".join(member.expertise_areas[:2])
|
|
1015
|
+
if len(member.expertise_areas) > 2:
|
|
1016
|
+
expertise_str += "..."
|
|
1017
|
+
|
|
1018
|
+
# Highlight selected row
|
|
1019
|
+
if self.state.interaction_mode == InteractionMode.SELECT and idx == self.state.selected_index:
|
|
1020
|
+
selector = ">"
|
|
1021
|
+
row_style = "bold"
|
|
1022
|
+
else:
|
|
1023
|
+
selector = " "
|
|
1024
|
+
row_style = None
|
|
1025
|
+
|
|
1026
|
+
members_table.add_row(
|
|
1027
|
+
selector,
|
|
1028
|
+
Text(member.name, style=row_style) if row_style else member.name,
|
|
1029
|
+
member.role.value.title(),
|
|
1030
|
+
member.department.value.title(),
|
|
1031
|
+
expertise_str,
|
|
1032
|
+
f"{member.voting_weight:.1f}"
|
|
1033
|
+
)
|
|
1034
|
+
|
|
1035
|
+
return Panel(
|
|
1036
|
+
members_table,
|
|
1037
|
+
title="Corporate Members",
|
|
1038
|
+
border_style="cyan",
|
|
1039
|
+
box=box.ROUNDED
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
def render_member_details(self, member_id: str) -> Panel:
|
|
1043
|
+
"""Render detailed member information."""
|
|
1044
|
+
if not self.corporate_swarm or member_id not in self.corporate_swarm.members:
|
|
1045
|
+
return Panel("Member not found", border_style="red")
|
|
1046
|
+
|
|
1047
|
+
member = self.corporate_swarm.members[member_id]
|
|
1048
|
+
|
|
1049
|
+
details_table = Table.grid(padding=(0, 2))
|
|
1050
|
+
details_table.add_column(style="cyan", justify="right", width=20)
|
|
1051
|
+
details_table.add_column(style="white")
|
|
1052
|
+
|
|
1053
|
+
details_table.add_row("Name:", member.name)
|
|
1054
|
+
details_table.add_row("Member ID:", member.member_id)
|
|
1055
|
+
details_table.add_row("Role:", member.role.value.title())
|
|
1056
|
+
details_table.add_row("Department:", member.department.value.title())
|
|
1057
|
+
details_table.add_row("Voting Weight:", f"{member.voting_weight:.1f}")
|
|
1058
|
+
details_table.add_row("Independence:", "Yes" if member.independence_status else "No")
|
|
1059
|
+
details_table.add_row("", "")
|
|
1060
|
+
details_table.add_row("Expertise Areas:", ", ".join(member.expertise_areas) if member.expertise_areas else "None")
|
|
1061
|
+
details_table.add_row("Committees:", str(len(member.board_committees)))
|
|
1062
|
+
|
|
1063
|
+
if member.agent:
|
|
1064
|
+
details_table.add_row("", "")
|
|
1065
|
+
details_table.add_row("Agent Status:", "Active")
|
|
1066
|
+
if hasattr(member.agent, 'agent_name'):
|
|
1067
|
+
details_table.add_row("Agent Name:", member.agent.agent_name)
|
|
1068
|
+
|
|
1069
|
+
return Panel(
|
|
1070
|
+
details_table,
|
|
1071
|
+
title=f"Member Details: {member.name}",
|
|
1072
|
+
border_style="cyan",
|
|
1073
|
+
box=box.ROUNDED
|
|
1074
|
+
)
|
|
1075
|
+
|
|
1076
|
+
def render_proposals_view(self) -> Panel:
|
|
1077
|
+
"""Render proposals view with selection."""
|
|
1078
|
+
if not self.corporate_swarm:
|
|
1079
|
+
return Panel("No CorporateSwarm instance", border_style="red")
|
|
1080
|
+
|
|
1081
|
+
proposals_table = Table(show_header=True, header_style="bold yellow", box=box.ROUNDED)
|
|
1082
|
+
proposals_table.add_column("", width=2)
|
|
1083
|
+
proposals_table.add_column("Title", style="cyan", width=30)
|
|
1084
|
+
proposals_table.add_column("Type", style="yellow", width=15)
|
|
1085
|
+
proposals_table.add_column("Department", style="green", width=15)
|
|
1086
|
+
proposals_table.add_column("Budget Impact", style="magenta", justify="right", width=15)
|
|
1087
|
+
proposals_table.add_column("Status", style="white", width=12)
|
|
1088
|
+
|
|
1089
|
+
for idx, proposal in enumerate(self.corporate_swarm.proposals):
|
|
1090
|
+
status_style = "green" if proposal.status == "approved" else "red" if proposal.status == "rejected" else "yellow"
|
|
1091
|
+
|
|
1092
|
+
if self.state.interaction_mode == InteractionMode.SELECT and idx == self.state.selected_index:
|
|
1093
|
+
selector = ">"
|
|
1094
|
+
row_style = "bold"
|
|
1095
|
+
else:
|
|
1096
|
+
selector = " "
|
|
1097
|
+
row_style = None
|
|
1098
|
+
|
|
1099
|
+
proposals_table.add_row(
|
|
1100
|
+
selector,
|
|
1101
|
+
Text(proposal.title[:28] + ("..." if len(proposal.title) > 28 else ""), style=row_style) if row_style else proposal.title[:28] + ("..." if len(proposal.title) > 28 else ""),
|
|
1102
|
+
proposal.proposal_type.value.replace("_", " ").title(),
|
|
1103
|
+
proposal.department.value.title(),
|
|
1104
|
+
f"${proposal.budget_impact:,.2f}",
|
|
1105
|
+
f"[{status_style}]{proposal.status.upper()}[/{status_style}]"
|
|
1106
|
+
)
|
|
1107
|
+
|
|
1108
|
+
return Panel(
|
|
1109
|
+
proposals_table,
|
|
1110
|
+
title="Proposals",
|
|
1111
|
+
border_style="yellow",
|
|
1112
|
+
box=box.ROUNDED
|
|
1113
|
+
)
|
|
1114
|
+
|
|
1115
|
+
def render_proposal_details(self, proposal_id: str) -> Panel:
|
|
1116
|
+
"""Render detailed proposal information."""
|
|
1117
|
+
if not self.corporate_swarm:
|
|
1118
|
+
return Panel("No CorporateSwarm instance", border_style="red")
|
|
1119
|
+
|
|
1120
|
+
proposal = None
|
|
1121
|
+
for p in self.corporate_swarm.proposals:
|
|
1122
|
+
if p.proposal_id == proposal_id:
|
|
1123
|
+
proposal = p
|
|
1124
|
+
break
|
|
1125
|
+
|
|
1126
|
+
if not proposal:
|
|
1127
|
+
return Panel("Proposal not found", border_style="red")
|
|
1128
|
+
|
|
1129
|
+
details_table = Table.grid(padding=(0, 2))
|
|
1130
|
+
details_table.add_column(style="cyan", justify="right", width=20)
|
|
1131
|
+
details_table.add_column(style="white")
|
|
1132
|
+
|
|
1133
|
+
details_table.add_row("Title:", proposal.title)
|
|
1134
|
+
details_table.add_row("Proposal ID:", proposal.proposal_id)
|
|
1135
|
+
details_table.add_row("Type:", proposal.proposal_type.value.replace("_", " ").title())
|
|
1136
|
+
details_table.add_row("Department:", proposal.department.value.title())
|
|
1137
|
+
details_table.add_row("Budget Impact:", f"${proposal.budget_impact:,.2f}")
|
|
1138
|
+
details_table.add_row("Status:", proposal.status.upper())
|
|
1139
|
+
details_table.add_row("Timeline:", proposal.timeline or "Not specified")
|
|
1140
|
+
details_table.add_row("", "")
|
|
1141
|
+
details_table.add_row("Description:", proposal.description[:200] + ("..." if len(proposal.description) > 200 else ""))
|
|
1142
|
+
|
|
1143
|
+
if proposal.causal_analysis:
|
|
1144
|
+
details_table.add_row("", "")
|
|
1145
|
+
details_table.add_row("Causal Analysis:", "Available")
|
|
1146
|
+
|
|
1147
|
+
if proposal.quant_analysis:
|
|
1148
|
+
details_table.add_row("Quantitative Analysis:", "Available")
|
|
1149
|
+
|
|
1150
|
+
if proposal.board_evaluations:
|
|
1151
|
+
details_table.add_row("Board Evaluations:", "Available")
|
|
1152
|
+
|
|
1153
|
+
return Panel(
|
|
1154
|
+
details_table,
|
|
1155
|
+
title=f"Proposal Details: {proposal.title}",
|
|
1156
|
+
border_style="yellow",
|
|
1157
|
+
box=box.ROUNDED
|
|
1158
|
+
)
|
|
1159
|
+
|
|
1160
|
+
def render_votes_view(self) -> Panel:
|
|
1161
|
+
"""Render votes view with selection."""
|
|
1162
|
+
if not self.corporate_swarm:
|
|
1163
|
+
return Panel("No CorporateSwarm instance", border_style="red")
|
|
1164
|
+
|
|
1165
|
+
votes_table = Table(show_header=True, header_style="bold green", box=box.ROUNDED)
|
|
1166
|
+
votes_table.add_column("", width=2)
|
|
1167
|
+
votes_table.add_column("Proposal", style="cyan", width=30)
|
|
1168
|
+
votes_table.add_column("Participants", style="yellow", justify="right", width=12)
|
|
1169
|
+
votes_table.add_column("Result", style="white", width=15)
|
|
1170
|
+
votes_table.add_column("Consensus", style="magenta", justify="right", width=12)
|
|
1171
|
+
votes_table.add_column("Time", style="dim", width=10)
|
|
1172
|
+
|
|
1173
|
+
for idx, vote in enumerate(self.corporate_swarm.votes[-10:]):
|
|
1174
|
+
result_style = "green" if vote.result.value == "approved" else "red" if vote.result.value == "rejected" else "yellow"
|
|
1175
|
+
consensus = vote.governance_consensus * 100
|
|
1176
|
+
|
|
1177
|
+
time_str = datetime.fromtimestamp(vote.timestamp).strftime("%H:%M:%S") if vote.timestamp else "N/A"
|
|
1178
|
+
|
|
1179
|
+
if self.state.interaction_mode == InteractionMode.SELECT and idx == self.state.selected_index:
|
|
1180
|
+
selector = ">"
|
|
1181
|
+
row_style = "bold"
|
|
1182
|
+
else:
|
|
1183
|
+
selector = " "
|
|
1184
|
+
row_style = None
|
|
1185
|
+
|
|
1186
|
+
votes_table.add_row(
|
|
1187
|
+
selector,
|
|
1188
|
+
Text(vote.proposal.title[:28] + ("..." if len(vote.proposal.title) > 28 else ""), style=row_style) if row_style else vote.proposal.title[:28] + ("..." if len(vote.proposal.title) > 28 else ""),
|
|
1189
|
+
str(len(vote.participants)),
|
|
1190
|
+
f"[{result_style}]{vote.result.value.upper()}[/{result_style}]",
|
|
1191
|
+
f"{consensus:.1f}%",
|
|
1192
|
+
time_str
|
|
1193
|
+
)
|
|
1194
|
+
|
|
1195
|
+
return Panel(
|
|
1196
|
+
votes_table,
|
|
1197
|
+
title="Voting History",
|
|
1198
|
+
border_style="green",
|
|
1199
|
+
box=box.ROUNDED
|
|
1200
|
+
)
|
|
1201
|
+
|
|
1202
|
+
def render_vote_details(self, vote_id: str) -> Panel:
|
|
1203
|
+
"""Render detailed vote information."""
|
|
1204
|
+
if not self.corporate_swarm:
|
|
1205
|
+
return Panel("No CorporateSwarm instance", border_style="red")
|
|
1206
|
+
|
|
1207
|
+
vote = None
|
|
1208
|
+
for v in self.corporate_swarm.votes:
|
|
1209
|
+
if v.vote_id == vote_id:
|
|
1210
|
+
vote = v
|
|
1211
|
+
break
|
|
1212
|
+
|
|
1213
|
+
if not vote:
|
|
1214
|
+
return Panel("Vote not found", border_style="red")
|
|
1215
|
+
|
|
1216
|
+
details_table = Table.grid(padding=(0, 2))
|
|
1217
|
+
details_table.add_column(style="cyan", justify="right", width=20)
|
|
1218
|
+
details_table.add_column(style="white")
|
|
1219
|
+
|
|
1220
|
+
details_table.add_row("Proposal:", vote.proposal.title)
|
|
1221
|
+
details_table.add_row("Vote ID:", vote.vote_id)
|
|
1222
|
+
details_table.add_row("Result:", vote.result.value.upper())
|
|
1223
|
+
details_table.add_row("Participants:", str(len(vote.participants)))
|
|
1224
|
+
details_table.add_row("Consensus:", f"{vote.governance_consensus * 100:.1f}%")
|
|
1225
|
+
details_table.add_row("Timestamp:", datetime.fromtimestamp(vote.timestamp).strftime("%Y-%m-%d %H:%M:%S") if vote.timestamp else "N/A")
|
|
1226
|
+
|
|
1227
|
+
if vote.causal_reasoning_summary:
|
|
1228
|
+
details_table.add_row("", "")
|
|
1229
|
+
details_table.add_row("Causal Reasoning:", vote.causal_reasoning_summary[:100] + ("..." if len(vote.causal_reasoning_summary) > 100 else ""))
|
|
1230
|
+
|
|
1231
|
+
if vote.quant_signals:
|
|
1232
|
+
details_table.add_row("Quant Signals:", f"{len(vote.quant_signals)} signals")
|
|
1233
|
+
|
|
1234
|
+
# Individual votes summary
|
|
1235
|
+
if vote.individual_votes:
|
|
1236
|
+
details_table.add_row("", "")
|
|
1237
|
+
details_table.add_row("Individual Votes:", "")
|
|
1238
|
+
approve_count = sum(1 for v in vote.individual_votes.values() if v.get("vote") == "APPROVE")
|
|
1239
|
+
reject_count = sum(1 for v in vote.individual_votes.values() if v.get("vote") == "REJECT")
|
|
1240
|
+
abstain_count = sum(1 for v in vote.individual_votes.values() if v.get("vote") == "ABSTAIN")
|
|
1241
|
+
|
|
1242
|
+
details_table.add_row(" Approve:", f"[green]{approve_count}[/green]")
|
|
1243
|
+
details_table.add_row(" Reject:", f"[red]{reject_count}[/red]")
|
|
1244
|
+
details_table.add_row(" Abstain:", f"[yellow]{abstain_count}[/yellow]")
|
|
1245
|
+
|
|
1246
|
+
return Panel(
|
|
1247
|
+
details_table,
|
|
1248
|
+
title=f"Vote Details: {vote.proposal.title}",
|
|
1249
|
+
border_style="green",
|
|
1250
|
+
box=box.ROUNDED
|
|
1251
|
+
)
|
|
1252
|
+
|
|
1253
|
+
def render_meetings_view(self) -> Panel:
|
|
1254
|
+
"""Render board meetings view with selection."""
|
|
1255
|
+
if not self.corporate_swarm:
|
|
1256
|
+
return Panel("No CorporateSwarm instance", border_style="red")
|
|
1257
|
+
|
|
1258
|
+
meetings_table = Table(show_header=True, header_style="bold blue", box=box.ROUNDED)
|
|
1259
|
+
meetings_table.add_column("", width=2)
|
|
1260
|
+
meetings_table.add_column("Type", style="cyan", width=20)
|
|
1261
|
+
meetings_table.add_column("Date", style="yellow", width=15)
|
|
1262
|
+
meetings_table.add_column("Attendees", style="green", justify="right", width=12)
|
|
1263
|
+
meetings_table.add_column("Quorum", style="white", width=10)
|
|
1264
|
+
meetings_table.add_column("Resolutions", style="magenta", justify="right", width=12)
|
|
1265
|
+
|
|
1266
|
+
for idx, meeting in enumerate(self.corporate_swarm.board_meetings[-10:]):
|
|
1267
|
+
date_str = datetime.fromtimestamp(meeting.date).strftime("%Y-%m-%d %H:%M") if meeting.date else "N/A"
|
|
1268
|
+
quorum_style = "green" if meeting.quorum_met else "red"
|
|
1269
|
+
quorum_symbol = "YES" if meeting.quorum_met else "NO"
|
|
1270
|
+
|
|
1271
|
+
if self.state.interaction_mode == InteractionMode.SELECT and idx == self.state.selected_index:
|
|
1272
|
+
selector = ">"
|
|
1273
|
+
row_style = "bold"
|
|
1274
|
+
else:
|
|
1275
|
+
selector = " "
|
|
1276
|
+
row_style = None
|
|
1277
|
+
|
|
1278
|
+
meetings_table.add_row(
|
|
1279
|
+
selector,
|
|
1280
|
+
Text(meeting.meeting_type.value.replace("_", " ").title(), style=row_style) if row_style else meeting.meeting_type.value.replace("_", " ").title(),
|
|
1281
|
+
date_str,
|
|
1282
|
+
str(len(meeting.attendees)),
|
|
1283
|
+
f"[{quorum_style}]{quorum_symbol}[/{quorum_style}]",
|
|
1284
|
+
str(len(meeting.resolutions))
|
|
1285
|
+
)
|
|
1286
|
+
|
|
1287
|
+
return Panel(
|
|
1288
|
+
meetings_table,
|
|
1289
|
+
title="Board Meetings",
|
|
1290
|
+
border_style="blue",
|
|
1291
|
+
box=box.ROUNDED
|
|
1292
|
+
)
|
|
1293
|
+
|
|
1294
|
+
def render_meeting_details(self, meeting_id: str) -> Panel:
|
|
1295
|
+
"""Render detailed meeting information."""
|
|
1296
|
+
if not self.corporate_swarm:
|
|
1297
|
+
return Panel("No CorporateSwarm instance", border_style="red")
|
|
1298
|
+
|
|
1299
|
+
meeting = None
|
|
1300
|
+
for m in self.corporate_swarm.board_meetings:
|
|
1301
|
+
if m.meeting_id == meeting_id:
|
|
1302
|
+
meeting = m
|
|
1303
|
+
break
|
|
1304
|
+
|
|
1305
|
+
if not meeting:
|
|
1306
|
+
return Panel("Meeting not found", border_style="red")
|
|
1307
|
+
|
|
1308
|
+
details_table = Table.grid(padding=(0, 2))
|
|
1309
|
+
details_table.add_column(style="cyan", justify="right", width=20)
|
|
1310
|
+
details_table.add_column(style="white")
|
|
1311
|
+
|
|
1312
|
+
details_table.add_row("Meeting ID:", meeting.meeting_id)
|
|
1313
|
+
details_table.add_row("Type:", meeting.meeting_type.value.replace("_", " ").title())
|
|
1314
|
+
details_table.add_row("Date:", datetime.fromtimestamp(meeting.date).strftime("%Y-%m-%d %H:%M:%S") if meeting.date else "N/A")
|
|
1315
|
+
details_table.add_row("Location:", meeting.location or "Virtual")
|
|
1316
|
+
details_table.add_row("Attendees:", str(len(meeting.attendees)))
|
|
1317
|
+
details_table.add_row("Quorum Met:", "Yes" if meeting.quorum_met else "No")
|
|
1318
|
+
details_table.add_row("Resolutions:", str(len(meeting.resolutions)))
|
|
1319
|
+
|
|
1320
|
+
if meeting.agenda:
|
|
1321
|
+
details_table.add_row("", "")
|
|
1322
|
+
details_table.add_row("Agenda:", "")
|
|
1323
|
+
for item in meeting.agenda[:5]:
|
|
1324
|
+
details_table.add_row("", f" • {item}")
|
|
1325
|
+
|
|
1326
|
+
if meeting.resolutions:
|
|
1327
|
+
details_table.add_row("", "")
|
|
1328
|
+
details_table.add_row("Resolutions:", "")
|
|
1329
|
+
for resolution in meeting.resolutions[:5]:
|
|
1330
|
+
details_table.add_row("", f" • {resolution[:60]}")
|
|
1331
|
+
|
|
1332
|
+
if meeting.minutes:
|
|
1333
|
+
details_table.add_row("", "")
|
|
1334
|
+
details_table.add_row("Minutes:", meeting.minutes[:200] + ("..." if len(meeting.minutes) > 200 else ""))
|
|
1335
|
+
|
|
1336
|
+
return Panel(
|
|
1337
|
+
details_table,
|
|
1338
|
+
title=f"Meeting Details: {meeting.meeting_type.value.replace('_', ' ').title()}",
|
|
1339
|
+
border_style="blue",
|
|
1340
|
+
box=box.ROUNDED
|
|
1341
|
+
)
|
|
1342
|
+
|
|
1343
|
+
def render_esg_view(self) -> Panel:
|
|
1344
|
+
"""Render ESG dashboard view."""
|
|
1345
|
+
status = self.state.status_data or {}
|
|
1346
|
+
esg = status.get("esg_governance", {})
|
|
1347
|
+
|
|
1348
|
+
esg_layout = Layout()
|
|
1349
|
+
esg_layout.split_column(
|
|
1350
|
+
Layout(name="scores", size=12),
|
|
1351
|
+
Layout(name="details", ratio=1)
|
|
1352
|
+
)
|
|
1353
|
+
|
|
1354
|
+
# Scores with progress bars
|
|
1355
|
+
scores_table = Table.grid(padding=(0, 2))
|
|
1356
|
+
scores_table.add_column(style="cyan", width=20)
|
|
1357
|
+
scores_table.add_column(style="white", width=50)
|
|
1358
|
+
scores_table.add_column(style="yellow", width=10)
|
|
1359
|
+
|
|
1360
|
+
overall = esg.get("overall_score", 0)
|
|
1361
|
+
env = esg.get("environmental_score", 0)
|
|
1362
|
+
social = esg.get("social_score", 0)
|
|
1363
|
+
gov = esg.get("governance_score", 0)
|
|
1364
|
+
|
|
1365
|
+
scores_table.add_row(
|
|
1366
|
+
"Overall ESG:",
|
|
1367
|
+
self._create_progress_bar(overall, 100),
|
|
1368
|
+
f"{overall:.1f}%"
|
|
1369
|
+
)
|
|
1370
|
+
|
|
1371
|
+
scores_table.add_row(
|
|
1372
|
+
"Environmental:",
|
|
1373
|
+
self._create_progress_bar(env, 100),
|
|
1374
|
+
f"{env:.1f}%"
|
|
1375
|
+
)
|
|
1376
|
+
|
|
1377
|
+
scores_table.add_row(
|
|
1378
|
+
"Social:",
|
|
1379
|
+
self._create_progress_bar(social, 100),
|
|
1380
|
+
f"{social:.1f}%"
|
|
1381
|
+
)
|
|
1382
|
+
|
|
1383
|
+
scores_table.add_row(
|
|
1384
|
+
"Governance:",
|
|
1385
|
+
self._create_progress_bar(gov, 100),
|
|
1386
|
+
f"{gov:.1f}%"
|
|
1387
|
+
)
|
|
1388
|
+
|
|
1389
|
+
esg_layout["scores"].update(
|
|
1390
|
+
Panel(scores_table, title="ESG Scores", border_style="green", box=box.ROUNDED)
|
|
1391
|
+
)
|
|
1392
|
+
|
|
1393
|
+
# Details
|
|
1394
|
+
details_table = Table.grid(padding=(0, 2))
|
|
1395
|
+
details_table.add_column(style="cyan", justify="right")
|
|
1396
|
+
details_table.add_column(style="white")
|
|
1397
|
+
|
|
1398
|
+
details_table.add_row("Carbon Footprint:", f"{esg.get('carbon_footprint', 0):.2f} tons CO2")
|
|
1399
|
+
details_table.add_row("Diversity Index:", f"{esg.get('diversity_index', 0):.2%}")
|
|
1400
|
+
details_table.add_row("Stakeholder Satisfaction:", f"{esg.get('stakeholder_satisfaction', 0):.1f}%")
|
|
1401
|
+
|
|
1402
|
+
goals = esg.get("sustainability_goals", [])
|
|
1403
|
+
if goals:
|
|
1404
|
+
details_table.add_row("", "")
|
|
1405
|
+
details_table.add_row("Sustainability Goals:", "")
|
|
1406
|
+
for goal in goals[:3]:
|
|
1407
|
+
details_table.add_row("", f" • {goal}")
|
|
1408
|
+
|
|
1409
|
+
esg_layout["details"].update(
|
|
1410
|
+
Panel(details_table, title="Details", border_style="green", box=box.ROUNDED)
|
|
1411
|
+
)
|
|
1412
|
+
|
|
1413
|
+
return Panel(
|
|
1414
|
+
esg_layout,
|
|
1415
|
+
title="ESG Dashboard",
|
|
1416
|
+
border_style="green",
|
|
1417
|
+
box=box.ROUNDED
|
|
1418
|
+
)
|
|
1419
|
+
|
|
1420
|
+
def render_risk_view(self) -> Panel:
|
|
1421
|
+
"""Render risk assessment view."""
|
|
1422
|
+
status = self.state.status_data or {}
|
|
1423
|
+
risk = status.get("risk_management", {})
|
|
1424
|
+
|
|
1425
|
+
if not self.corporate_swarm or not self.corporate_swarm.risk_assessments:
|
|
1426
|
+
return Panel("No risk assessments available. Use command 'risk' to conduct assessment.", border_style="yellow")
|
|
1427
|
+
|
|
1428
|
+
risk_table = Table(show_header=True, header_style="bold red", box=box.ROUNDED)
|
|
1429
|
+
risk_table.add_column("Category", style="cyan", width=20)
|
|
1430
|
+
risk_table.add_column("Level", style="white", width=12)
|
|
1431
|
+
risk_table.add_column("Probability", style="yellow", justify="right", width=12)
|
|
1432
|
+
risk_table.add_column("Impact", style="magenta", justify="right", width=12)
|
|
1433
|
+
risk_table.add_column("Score", style="red", justify="right", width=12)
|
|
1434
|
+
risk_table.add_column("Owner", style="green", width=15)
|
|
1435
|
+
|
|
1436
|
+
for risk_id, assessment in list(self.corporate_swarm.risk_assessments.items())[:15]:
|
|
1437
|
+
level_style = "red" if assessment.risk_level == "high" else "yellow" if assessment.risk_level == "medium" else "green"
|
|
1438
|
+
|
|
1439
|
+
risk_table.add_row(
|
|
1440
|
+
assessment.risk_category.title(),
|
|
1441
|
+
f"[{level_style}]{assessment.risk_level.upper()}[/{level_style}]",
|
|
1442
|
+
f"{assessment.probability:.2%}",
|
|
1443
|
+
f"{assessment.impact:.2%}",
|
|
1444
|
+
f"{assessment.risk_score:.2%}",
|
|
1445
|
+
assessment.owner
|
|
1446
|
+
)
|
|
1447
|
+
|
|
1448
|
+
return Panel(
|
|
1449
|
+
risk_table,
|
|
1450
|
+
title="Risk Assessment",
|
|
1451
|
+
border_style="red",
|
|
1452
|
+
box=box.ROUNDED
|
|
1453
|
+
)
|
|
1454
|
+
|
|
1455
|
+
def render_aop_view(self) -> Panel:
|
|
1456
|
+
"""Render AOP integration view."""
|
|
1457
|
+
status = self.state.status_data or {}
|
|
1458
|
+
aop = status.get("aop_integration", {})
|
|
1459
|
+
|
|
1460
|
+
aop_layout = Layout()
|
|
1461
|
+
aop_layout.split_column(
|
|
1462
|
+
Layout(name="status", size=10),
|
|
1463
|
+
Layout(name="queues", ratio=1)
|
|
1464
|
+
)
|
|
1465
|
+
|
|
1466
|
+
# Status
|
|
1467
|
+
status_table = Table.grid(padding=(0, 2))
|
|
1468
|
+
status_table.add_column(style="cyan", justify="right", width=25)
|
|
1469
|
+
status_table.add_column(style="white")
|
|
1470
|
+
|
|
1471
|
+
status_table.add_row(
|
|
1472
|
+
"AOP Enabled:",
|
|
1473
|
+
"Yes" if aop.get("enabled", False) else "No"
|
|
1474
|
+
)
|
|
1475
|
+
|
|
1476
|
+
status_table.add_row(
|
|
1477
|
+
"Queue Execution:",
|
|
1478
|
+
"Yes" if aop.get("queue_execution_enabled", False) else "No"
|
|
1479
|
+
)
|
|
1480
|
+
|
|
1481
|
+
status_table.add_row(
|
|
1482
|
+
"AOP Server Active:",
|
|
1483
|
+
"Yes" if aop.get("aop_server_active", False) else "No"
|
|
1484
|
+
)
|
|
1485
|
+
|
|
1486
|
+
status_table.add_row(
|
|
1487
|
+
"Member Queues:",
|
|
1488
|
+
str(aop.get("total_member_queues", 0))
|
|
1489
|
+
)
|
|
1490
|
+
|
|
1491
|
+
server_info = aop.get("aop_server", {})
|
|
1492
|
+
if server_info:
|
|
1493
|
+
status_table.add_row("", "")
|
|
1494
|
+
status_table.add_row("Server Port:", str(server_info.get("port", "N/A")))
|
|
1495
|
+
status_table.add_row("Total Agents:", str(server_info.get("total_agents", 0)))
|
|
1496
|
+
|
|
1497
|
+
aop_layout["status"].update(
|
|
1498
|
+
Panel(status_table, title="AOP Status", border_style="magenta", box=box.ROUNDED)
|
|
1499
|
+
)
|
|
1500
|
+
|
|
1501
|
+
# Queue Stats
|
|
1502
|
+
queue_stats = aop.get("member_queue_stats", {})
|
|
1503
|
+
if queue_stats:
|
|
1504
|
+
queues_table = Table(show_header=True, header_style="bold magenta", box=box.ROUNDED)
|
|
1505
|
+
queues_table.add_column("Member", style="cyan", width=20)
|
|
1506
|
+
queues_table.add_column("Total Tasks", style="yellow", justify="right", width=12)
|
|
1507
|
+
queues_table.add_column("Completed", style="green", justify="right", width=12)
|
|
1508
|
+
queues_table.add_column("Pending", style="yellow", justify="right", width=12)
|
|
1509
|
+
queues_table.add_column("Status", style="white", width=12)
|
|
1510
|
+
|
|
1511
|
+
for member_name, stats in list(queue_stats.items())[:10]:
|
|
1512
|
+
queues_table.add_row(
|
|
1513
|
+
member_name[:18] + ("..." if len(member_name) > 18 else ""),
|
|
1514
|
+
str(stats.get("total_tasks", 0)),
|
|
1515
|
+
str(stats.get("completed_tasks", 0)),
|
|
1516
|
+
str(stats.get("pending_tasks", 0)),
|
|
1517
|
+
stats.get("queue_status", "unknown")
|
|
1518
|
+
)
|
|
1519
|
+
|
|
1520
|
+
aop_layout["queues"].update(
|
|
1521
|
+
Panel(queues_table, title="Queue Statistics", border_style="magenta", box=box.ROUNDED)
|
|
1522
|
+
)
|
|
1523
|
+
else:
|
|
1524
|
+
aop_layout["queues"].update(
|
|
1525
|
+
Panel("No queue statistics available", border_style="dim", box=box.ROUNDED)
|
|
1526
|
+
)
|
|
1527
|
+
|
|
1528
|
+
return Panel(
|
|
1529
|
+
aop_layout,
|
|
1530
|
+
title="AOP Integration",
|
|
1531
|
+
border_style="magenta",
|
|
1532
|
+
box=box.ROUNDED
|
|
1533
|
+
)
|
|
1534
|
+
|
|
1535
|
+
def render_committees_view(self) -> Panel:
|
|
1536
|
+
"""Render board committees view."""
|
|
1537
|
+
if not self.corporate_swarm:
|
|
1538
|
+
return Panel("No CorporateSwarm instance", border_style="red")
|
|
1539
|
+
|
|
1540
|
+
committees_table = Table(show_header=True, header_style="bold blue", box=box.ROUNDED)
|
|
1541
|
+
committees_table.add_column("Name", style="cyan", width=25)
|
|
1542
|
+
committees_table.add_column("Type", style="yellow", width=20)
|
|
1543
|
+
committees_table.add_column("Chair", style="green", width=20)
|
|
1544
|
+
committees_table.add_column("Members", style="white", justify="right", width=10)
|
|
1545
|
+
|
|
1546
|
+
for committee_id, committee in self.corporate_swarm.board_committees.items():
|
|
1547
|
+
chair_name = "Unknown"
|
|
1548
|
+
if committee.chair and committee.chair in self.corporate_swarm.members:
|
|
1549
|
+
chair_name = self.corporate_swarm.members[committee.chair].name
|
|
1550
|
+
|
|
1551
|
+
committees_table.add_row(
|
|
1552
|
+
committee.name,
|
|
1553
|
+
committee.committee_type.value.replace("_", " ").title(),
|
|
1554
|
+
chair_name[:18] + ("..." if len(chair_name) > 18 else ""),
|
|
1555
|
+
str(len(committee.members))
|
|
1556
|
+
)
|
|
1557
|
+
|
|
1558
|
+
return Panel(
|
|
1559
|
+
committees_table,
|
|
1560
|
+
title="Board Committees",
|
|
1561
|
+
border_style="blue",
|
|
1562
|
+
box=box.ROUNDED
|
|
1563
|
+
)
|
|
1564
|
+
|
|
1565
|
+
def render_departments_view(self) -> Panel:
|
|
1566
|
+
"""Render departments view."""
|
|
1567
|
+
if not self.corporate_swarm:
|
|
1568
|
+
return Panel("No CorporateSwarm instance", border_style="red")
|
|
1569
|
+
|
|
1570
|
+
dept_table = Table(show_header=True, header_style="bold green", box=box.ROUNDED)
|
|
1571
|
+
dept_table.add_column("Name", style="cyan", width=25)
|
|
1572
|
+
dept_table.add_column("Type", style="yellow", width=20)
|
|
1573
|
+
dept_table.add_column("Head", style="green", width=20)
|
|
1574
|
+
dept_table.add_column("Budget", style="magenta", justify="right", width=15)
|
|
1575
|
+
dept_table.add_column("Members", style="white", justify="right", width=10)
|
|
1576
|
+
|
|
1577
|
+
for dept_id, dept in self.corporate_swarm.departments.items():
|
|
1578
|
+
head_name = "Vacant"
|
|
1579
|
+
if dept.head and dept.head in self.corporate_swarm.members:
|
|
1580
|
+
head_name = self.corporate_swarm.members[dept.head].name
|
|
1581
|
+
|
|
1582
|
+
dept_table.add_row(
|
|
1583
|
+
dept.name,
|
|
1584
|
+
dept.department_type.value.replace("_", " ").title(),
|
|
1585
|
+
head_name[:18] + ("..." if len(head_name) > 18 else ""),
|
|
1586
|
+
f"${dept.budget:,.2f}",
|
|
1587
|
+
str(len(dept.members))
|
|
1588
|
+
)
|
|
1589
|
+
|
|
1590
|
+
return Panel(
|
|
1591
|
+
dept_table,
|
|
1592
|
+
title="Departments",
|
|
1593
|
+
border_style="green",
|
|
1594
|
+
box=box.ROUNDED
|
|
1595
|
+
)
|
|
1596
|
+
|
|
1597
|
+
def render(self) -> Layout:
|
|
1598
|
+
"""Render the complete TUI layout."""
|
|
1599
|
+
layout = self.create_layout()
|
|
1600
|
+
|
|
1601
|
+
layout["header"].update(self.render_header())
|
|
1602
|
+
layout["footer"].update(self.render_footer())
|
|
1603
|
+
|
|
1604
|
+
# Render main content based on view mode
|
|
1605
|
+
if self.state.view_mode == ViewMode.DASHBOARD:
|
|
1606
|
+
main_content = self.render_dashboard()
|
|
1607
|
+
layout["left"].update(main_content["overview"])
|
|
1608
|
+
layout["right"].split_column(
|
|
1609
|
+
Layout(main_content["metrics"]),
|
|
1610
|
+
Layout(main_content["recent"])
|
|
1611
|
+
)
|
|
1612
|
+
elif self.state.view_mode == ViewMode.MEMBERS:
|
|
1613
|
+
layout["left"].update(self.render_members_view())
|
|
1614
|
+
if self.state.selected_member_id:
|
|
1615
|
+
layout["right"].update(self.render_member_details(self.state.selected_member_id))
|
|
1616
|
+
else:
|
|
1617
|
+
layout["right"].update(
|
|
1618
|
+
Panel(
|
|
1619
|
+
"Select a member to view details\n\nPress [S] to enter selection mode\nPress [Enter] to view details",
|
|
1620
|
+
border_style="dim",
|
|
1621
|
+
box=box.ROUNDED
|
|
1622
|
+
)
|
|
1623
|
+
)
|
|
1624
|
+
elif self.state.view_mode == ViewMode.PROPOSALS:
|
|
1625
|
+
layout["left"].update(self.render_proposals_view())
|
|
1626
|
+
if self.state.selected_proposal_id:
|
|
1627
|
+
layout["right"].update(self.render_proposal_details(self.state.selected_proposal_id))
|
|
1628
|
+
else:
|
|
1629
|
+
layout["right"].update(
|
|
1630
|
+
Panel(
|
|
1631
|
+
"Select a proposal to view details\n\nPress [S] to enter selection mode\nPress [N] to create new proposal\nPress [Enter] to view details",
|
|
1632
|
+
border_style="dim",
|
|
1633
|
+
box=box.ROUNDED
|
|
1634
|
+
)
|
|
1635
|
+
)
|
|
1636
|
+
elif self.state.view_mode == ViewMode.VOTES:
|
|
1637
|
+
layout["left"].update(self.render_votes_view())
|
|
1638
|
+
if self.state.selected_vote_id:
|
|
1639
|
+
layout["right"].update(self.render_vote_details(self.state.selected_vote_id))
|
|
1640
|
+
else:
|
|
1641
|
+
layout["right"].update(
|
|
1642
|
+
Panel(
|
|
1643
|
+
"Select a vote to view details\n\nPress [S] to enter selection mode\nPress [Enter] to view details",
|
|
1644
|
+
border_style="dim",
|
|
1645
|
+
box=box.ROUNDED
|
|
1646
|
+
)
|
|
1647
|
+
)
|
|
1648
|
+
elif self.state.view_mode == ViewMode.MEETINGS:
|
|
1649
|
+
layout["left"].update(self.render_meetings_view())
|
|
1650
|
+
if self.state.selected_meeting_id:
|
|
1651
|
+
layout["right"].update(self.render_meeting_details(self.state.selected_meeting_id))
|
|
1652
|
+
else:
|
|
1653
|
+
layout["right"].update(
|
|
1654
|
+
Panel(
|
|
1655
|
+
"Select a meeting to view details\n\nPress [S] to enter selection mode\nPress [N] to schedule new meeting\nPress [Enter] to view details",
|
|
1656
|
+
border_style="dim",
|
|
1657
|
+
box=box.ROUNDED
|
|
1658
|
+
)
|
|
1659
|
+
)
|
|
1660
|
+
elif self.state.view_mode == ViewMode.ESG:
|
|
1661
|
+
layout["left"].update(self.render_esg_view())
|
|
1662
|
+
layout["right"].update(
|
|
1663
|
+
Panel(
|
|
1664
|
+
"ESG Dashboard\n\nUse command 'esg' to recalculate scores",
|
|
1665
|
+
border_style="dim",
|
|
1666
|
+
box=box.ROUNDED
|
|
1667
|
+
)
|
|
1668
|
+
)
|
|
1669
|
+
elif self.state.view_mode == ViewMode.RISK:
|
|
1670
|
+
layout["left"].update(self.render_risk_view())
|
|
1671
|
+
layout["right"].update(
|
|
1672
|
+
Panel(
|
|
1673
|
+
"Risk Assessment\n\nUse command 'risk' to conduct new assessment",
|
|
1674
|
+
border_style="dim",
|
|
1675
|
+
box=box.ROUNDED
|
|
1676
|
+
)
|
|
1677
|
+
)
|
|
1678
|
+
elif self.state.view_mode == ViewMode.AOP:
|
|
1679
|
+
layout["left"].update(self.render_aop_view())
|
|
1680
|
+
layout["right"].update(
|
|
1681
|
+
Panel(
|
|
1682
|
+
"AOP Integration Status\n\nMonitor queue statistics and server status",
|
|
1683
|
+
border_style="dim",
|
|
1684
|
+
box=box.ROUNDED
|
|
1685
|
+
)
|
|
1686
|
+
)
|
|
1687
|
+
elif self.state.view_mode == ViewMode.COMMITTEES:
|
|
1688
|
+
layout["left"].update(self.render_committees_view())
|
|
1689
|
+
layout["right"].update(
|
|
1690
|
+
Panel(
|
|
1691
|
+
"Board Committees\n\nView committee structure and membership",
|
|
1692
|
+
border_style="dim",
|
|
1693
|
+
box=box.ROUNDED
|
|
1694
|
+
)
|
|
1695
|
+
)
|
|
1696
|
+
elif self.state.view_mode == ViewMode.DEPARTMENTS:
|
|
1697
|
+
layout["left"].update(self.render_departments_view())
|
|
1698
|
+
layout["right"].update(
|
|
1699
|
+
Panel(
|
|
1700
|
+
"Departments\n\nView department structure and budgets",
|
|
1701
|
+
border_style="dim",
|
|
1702
|
+
box=box.ROUNDED
|
|
1703
|
+
)
|
|
1704
|
+
)
|
|
1705
|
+
|
|
1706
|
+
return layout
|
|
1707
|
+
|
|
1708
|
+
def _colorize_score(self, score: float, low_threshold: float = 70, high_threshold: float = 85) -> Text:
|
|
1709
|
+
"""Colorize a score based on thresholds."""
|
|
1710
|
+
if score >= high_threshold:
|
|
1711
|
+
style = "green"
|
|
1712
|
+
elif score >= low_threshold:
|
|
1713
|
+
style = "yellow"
|
|
1714
|
+
else:
|
|
1715
|
+
style = "red"
|
|
1716
|
+
|
|
1717
|
+
return Text(f"{score:.1f}%", style=style)
|
|
1718
|
+
|
|
1719
|
+
def _create_progress_bar(self, value: float, max_value: float = 100) -> Text:
|
|
1720
|
+
"""Create a text-based progress bar."""
|
|
1721
|
+
if max_value == 0:
|
|
1722
|
+
return Text("N/A", style="dim")
|
|
1723
|
+
|
|
1724
|
+
percentage = min(100, max(0, (value / max_value) * 100))
|
|
1725
|
+
bar_width = 40
|
|
1726
|
+
filled = int((percentage / 100) * bar_width)
|
|
1727
|
+
empty = bar_width - filled
|
|
1728
|
+
|
|
1729
|
+
if percentage >= 85:
|
|
1730
|
+
color = "green"
|
|
1731
|
+
elif percentage >= 70:
|
|
1732
|
+
color = "yellow"
|
|
1733
|
+
else:
|
|
1734
|
+
color = "red"
|
|
1735
|
+
|
|
1736
|
+
bar = "█" * filled + "░" * empty
|
|
1737
|
+
return Text(bar, style=color)
|
|
1738
|
+
|
|
1739
|
+
def _setup_keyboard_listener(self) -> None:
|
|
1740
|
+
"""Setup keyboard listener thread."""
|
|
1741
|
+
if sys.platform != "win32":
|
|
1742
|
+
try:
|
|
1743
|
+
# Save terminal settings
|
|
1744
|
+
self._old_terminal_settings = termios.tcgetattr(sys.stdin.fileno())
|
|
1745
|
+
tty.setcbreak(sys.stdin.fileno())
|
|
1746
|
+
except Exception:
|
|
1747
|
+
self._old_terminal_settings = None
|
|
1748
|
+
|
|
1749
|
+
self._key_queue = []
|
|
1750
|
+
self._keyboard_thread = threading.Thread(target=self._keyboard_listener, daemon=True)
|
|
1751
|
+
self._keyboard_thread.start()
|
|
1752
|
+
|
|
1753
|
+
def _keyboard_listener(self) -> None:
|
|
1754
|
+
"""Keyboard listener thread."""
|
|
1755
|
+
try:
|
|
1756
|
+
while self.is_running and not self._should_quit:
|
|
1757
|
+
key = self._get_key()
|
|
1758
|
+
if key:
|
|
1759
|
+
# Handle escape sequences for arrow keys
|
|
1760
|
+
if key == '\x1b':
|
|
1761
|
+
try:
|
|
1762
|
+
if sys.platform != "win32":
|
|
1763
|
+
if select.select([sys.stdin], [], [], 0.1) == ([sys.stdin], [], []):
|
|
1764
|
+
seq = sys.stdin.read(2)
|
|
1765
|
+
if seq == '[A':
|
|
1766
|
+
key = 'up'
|
|
1767
|
+
elif seq == '[B':
|
|
1768
|
+
key = 'down'
|
|
1769
|
+
elif seq == '[C':
|
|
1770
|
+
key = 'right'
|
|
1771
|
+
elif seq == '[D':
|
|
1772
|
+
key = 'left'
|
|
1773
|
+
else:
|
|
1774
|
+
key = 'escape'
|
|
1775
|
+
else:
|
|
1776
|
+
key = 'escape'
|
|
1777
|
+
else:
|
|
1778
|
+
key = 'escape'
|
|
1779
|
+
except Exception:
|
|
1780
|
+
key = 'escape'
|
|
1781
|
+
|
|
1782
|
+
self._key_queue.append(key)
|
|
1783
|
+
time.sleep(0.05)
|
|
1784
|
+
except Exception as e:
|
|
1785
|
+
logger.debug(f"Keyboard listener error: {e}")
|
|
1786
|
+
|
|
1787
|
+
def _get_key(self) -> Optional[str]:
|
|
1788
|
+
"""Get a key press from stdin (non-blocking)."""
|
|
1789
|
+
if sys.platform == "win32":
|
|
1790
|
+
try:
|
|
1791
|
+
import msvcrt
|
|
1792
|
+
if msvcrt.kbhit():
|
|
1793
|
+
key = msvcrt.getch()
|
|
1794
|
+
if isinstance(key, bytes):
|
|
1795
|
+
key = key.decode('utf-8', errors='ignore')
|
|
1796
|
+
return key
|
|
1797
|
+
except Exception:
|
|
1798
|
+
pass
|
|
1799
|
+
else:
|
|
1800
|
+
try:
|
|
1801
|
+
if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []):
|
|
1802
|
+
key = sys.stdin.read(1)
|
|
1803
|
+
return key
|
|
1804
|
+
except Exception:
|
|
1805
|
+
pass
|
|
1806
|
+
return None
|
|
1807
|
+
|
|
1808
|
+
def _process_keys(self) -> None:
|
|
1809
|
+
"""Process queued key presses."""
|
|
1810
|
+
if not self._key_queue:
|
|
1811
|
+
return
|
|
1812
|
+
|
|
1813
|
+
while self._key_queue:
|
|
1814
|
+
key = self._key_queue.pop(0)
|
|
1815
|
+
|
|
1816
|
+
# Handle command mode
|
|
1817
|
+
if self.state.interaction_mode == InteractionMode.COMMAND:
|
|
1818
|
+
if key == '\r' or key == '\n':
|
|
1819
|
+
self._activate_selected()
|
|
1820
|
+
elif key == '\x1b' or key == 'escape':
|
|
1821
|
+
self._exit_interaction()
|
|
1822
|
+
elif key == '\x7f' or key == '\b': # Backspace
|
|
1823
|
+
if self.state.current_command:
|
|
1824
|
+
self.state.current_command = self.state.current_command[:-1]
|
|
1825
|
+
elif len(key) == 1 and key.isprintable() and ord(key) >= 32:
|
|
1826
|
+
self.state.current_command += key
|
|
1827
|
+
continue
|
|
1828
|
+
|
|
1829
|
+
# Handle normal key presses
|
|
1830
|
+
handler = self._key_handlers.get(key)
|
|
1831
|
+
if handler:
|
|
1832
|
+
try:
|
|
1833
|
+
handler()
|
|
1834
|
+
except Exception as e:
|
|
1835
|
+
logger.error(f"Error handling key {key}: {e}")
|
|
1836
|
+
|
|
1837
|
+
def run_live(
|
|
1838
|
+
self,
|
|
1839
|
+
update_callback: Optional[Callable] = None,
|
|
1840
|
+
refresh_rate: Optional[float] = None
|
|
1841
|
+
) -> None:
|
|
1842
|
+
"""
|
|
1843
|
+
Run the TUI with live updates and keyboard interaction.
|
|
1844
|
+
|
|
1845
|
+
Args:
|
|
1846
|
+
update_callback: Optional callback function to call before each update
|
|
1847
|
+
refresh_rate: Optional refresh rate override
|
|
1848
|
+
"""
|
|
1849
|
+
if refresh_rate:
|
|
1850
|
+
self.state.refresh_rate = refresh_rate
|
|
1851
|
+
|
|
1852
|
+
self.is_running = True
|
|
1853
|
+
self._should_quit = False
|
|
1854
|
+
|
|
1855
|
+
# Setup keyboard listener
|
|
1856
|
+
self._setup_keyboard_listener()
|
|
1857
|
+
|
|
1858
|
+
try:
|
|
1859
|
+
with Live(
|
|
1860
|
+
self.render(),
|
|
1861
|
+
refresh_per_second=1.0 / self.state.refresh_rate,
|
|
1862
|
+
screen=True
|
|
1863
|
+
) as live:
|
|
1864
|
+
while not self._should_quit:
|
|
1865
|
+
# Process keyboard input
|
|
1866
|
+
self._process_keys()
|
|
1867
|
+
|
|
1868
|
+
if update_callback:
|
|
1869
|
+
update_callback(self)
|
|
1870
|
+
|
|
1871
|
+
if self.state.auto_refresh:
|
|
1872
|
+
self._update_status()
|
|
1873
|
+
|
|
1874
|
+
# Clear message after a delay
|
|
1875
|
+
if self.state.message and time.time() - self.state.last_update > 3:
|
|
1876
|
+
self.state.message = None
|
|
1877
|
+
|
|
1878
|
+
live.update(self.render())
|
|
1879
|
+
time.sleep(self.state.refresh_rate)
|
|
1880
|
+
except KeyboardInterrupt:
|
|
1881
|
+
pass
|
|
1882
|
+
finally:
|
|
1883
|
+
self.is_running = False
|
|
1884
|
+
if sys.platform != "win32" and hasattr(self, '_old_terminal_settings'):
|
|
1885
|
+
try:
|
|
1886
|
+
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, self._old_terminal_settings)
|
|
1887
|
+
except:
|
|
1888
|
+
pass
|
|
1889
|
+
self.console.clear()
|
|
1890
|
+
|
|
1891
|
+
def run_once(self) -> None:
|
|
1892
|
+
"""Render the TUI once without live updates."""
|
|
1893
|
+
self._update_status()
|
|
1894
|
+
self.console.print(self.render())
|
|
1895
|
+
|
|
1896
|
+
|
|
1897
|
+
def create_corporate_tui(corporate_swarm: Any, **kwargs) -> CorporateSwarmTUI:
|
|
1898
|
+
"""
|
|
1899
|
+
Create a CorporateSwarm TUI instance.
|
|
1900
|
+
|
|
1901
|
+
Args:
|
|
1902
|
+
corporate_swarm: CorporateSwarm instance
|
|
1903
|
+
**kwargs: Additional arguments for TUI initialization
|
|
1904
|
+
|
|
1905
|
+
Returns:
|
|
1906
|
+
CorporateSwarmTUI instance
|
|
1907
|
+
"""
|
|
1908
|
+
return CorporateSwarmTUI(corporate_swarm, **kwargs)
|