agentic-project-kit 1.0.1__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.
- agentic_project_kit/__init__.py +1 -0
- agentic_project_kit/absolute_path_portability_audit.py +241 -0
- agentic_project_kit/access_level_policy.py +14 -0
- agentic_project_kit/access_levels.py +22 -0
- agentic_project_kit/action_registry.py +195 -0
- agentic_project_kit/action_specs.py +286 -0
- agentic_project_kit/agent_command_runner.py +516 -0
- agentic_project_kit/audit_test_matrix.py +36 -0
- agentic_project_kit/audit_test_matrix_cli.py +43 -0
- agentic_project_kit/chat_bootloader.py +257 -0
- agentic_project_kit/chat_entrypoint_contract.py +297 -0
- agentic_project_kit/checks.py +511 -0
- agentic_project_kit/ci_readiness.py +211 -0
- agentic_project_kit/cli.py +147 -0
- agentic_project_kit/cli_commands/__init__.py +1 -0
- agentic_project_kit/cli_commands/absolute_path_audit.py +25 -0
- agentic_project_kit/cli_commands/actions.py +21 -0
- agentic_project_kit/cli_commands/artifact_gc.py +240 -0
- agentic_project_kit/cli_commands/boot.py +55 -0
- agentic_project_kit/cli_commands/chat.py +34 -0
- agentic_project_kit/cli_commands/checks.py +305 -0
- agentic_project_kit/cli_commands/cockpit.py +103 -0
- agentic_project_kit/cli_commands/command_taxonomy.py +25 -0
- agentic_project_kit/cli_commands/commands.py +129 -0
- agentic_project_kit/cli_commands/dev.py +23 -0
- agentic_project_kit/cli_commands/direction.py +92 -0
- agentic_project_kit/cli_commands/doc_currency_audit.py +25 -0
- agentic_project_kit/cli_commands/doc_registry.py +126 -0
- agentic_project_kit/cli_commands/docs.py +223 -0
- agentic_project_kit/cli_commands/dpa.py +1338 -0
- agentic_project_kit/cli_commands/evidence.py +251 -0
- agentic_project_kit/cli_commands/github.py +34 -0
- agentic_project_kit/cli_commands/governance.py +14 -0
- agentic_project_kit/cli_commands/gui.py +23 -0
- agentic_project_kit/cli_commands/gui_readiness_gate.py +27 -0
- agentic_project_kit/cli_commands/handoff.py +183 -0
- agentic_project_kit/cli_commands/human_workflows.py +430 -0
- agentic_project_kit/cli_commands/init.py +132 -0
- agentic_project_kit/cli_commands/instruction.py +75 -0
- agentic_project_kit/cli_commands/mutation_lock_audit.py +24 -0
- agentic_project_kit/cli_commands/ns_legacy_audit.py +25 -0
- agentic_project_kit/cli_commands/pass_already_done.py +66 -0
- agentic_project_kit/cli_commands/patch_failure_discipline_audit.py +26 -0
- agentic_project_kit/cli_commands/patch_preflight.py +37 -0
- agentic_project_kit/cli_commands/path_literal_audit.py +39 -0
- agentic_project_kit/cli_commands/patterns.py +40 -0
- agentic_project_kit/cli_commands/planning_docs_audit.py +25 -0
- agentic_project_kit/cli_commands/pr.py +133 -0
- agentic_project_kit/cli_commands/pr_hygiene.py +27 -0
- agentic_project_kit/cli_commands/profiles.py +26 -0
- agentic_project_kit/cli_commands/program_redundancy_audit.py +25 -0
- agentic_project_kit/cli_commands/project_direction.py +25 -0
- agentic_project_kit/cli_commands/release.py +392 -0
- agentic_project_kit/cli_commands/release_publish_orchestration.py +36 -0
- agentic_project_kit/cli_commands/remote_branch_hygiene.py +152 -0
- agentic_project_kit/cli_commands/remote_next.py +57 -0
- agentic_project_kit/cli_commands/removed_ns_commands.py +65 -0
- agentic_project_kit/cli_commands/rule_registry.py +129 -0
- agentic_project_kit/cli_commands/rules.py +222 -0
- agentic_project_kit/cli_commands/scaffold.py +46 -0
- agentic_project_kit/cli_commands/slice.py +21 -0
- agentic_project_kit/cli_commands/standard_gates_audit_suite.py +27 -0
- agentic_project_kit/cli_commands/state.py +51 -0
- agentic_project_kit/cli_commands/status_current_state_audit.py +30 -0
- agentic_project_kit/cli_commands/todo.py +53 -0
- agentic_project_kit/cli_commands/transfer.py +19 -0
- agentic_project_kit/cli_commands/transfer_context_flow.py +398 -0
- agentic_project_kit/cli_commands/transfer_context_helpers.py +398 -0
- agentic_project_kit/cli_commands/transfer_diagnostics.py +767 -0
- agentic_project_kit/cli_commands/transfer_evidence_flow.py +368 -0
- agentic_project_kit/cli_commands/transfer_handoff_flow.py +594 -0
- agentic_project_kit/cli_commands/transfer_order_contract.py +74 -0
- agentic_project_kit/cli_commands/transfer_post_merge_complete.py +442 -0
- agentic_project_kit/cli_commands/transfer_post_merge_settle.py +171 -0
- agentic_project_kit/cli_commands/transfer_pr_closeout_complete.py +124 -0
- agentic_project_kit/cli_commands/transfer_pr_create_flow.py +743 -0
- agentic_project_kit/cli_commands/transfer_pr_merge_flow.py +547 -0
- agentic_project_kit/cli_commands/transfer_repo_after_pr.py +160 -0
- agentic_project_kit/cli_commands/transfer_repo_pre_pr.py +664 -0
- agentic_project_kit/cli_commands/transfer_runner_flow.py +131 -0
- agentic_project_kit/cli_commands/transfer_runtime_initial.py +194 -0
- agentic_project_kit/cli_commands/transfer_shared.py +274 -0
- agentic_project_kit/cli_commands/validation.py +200 -0
- agentic_project_kit/cli_commands/version.py +27 -0
- agentic_project_kit/cli_commands/work_orders.py +219 -0
- agentic_project_kit/cli_commands/workflow.py +471 -0
- agentic_project_kit/cli_commands/workflow_guard.py +26 -0
- agentic_project_kit/cli_commands/workspace.py +252 -0
- agentic_project_kit/cockpit.py +608 -0
- agentic_project_kit/cockpit_readiness.py +67 -0
- agentic_project_kit/command_authority_audit.py +192 -0
- agentic_project_kit/command_inbox_check.py +103 -0
- agentic_project_kit/command_manifest.py +591 -0
- agentic_project_kit/command_queue_contract.py +161 -0
- agentic_project_kit/command_selector.py +245 -0
- agentic_project_kit/command_taxonomy.py +264 -0
- agentic_project_kit/commit_guard.py +67 -0
- agentic_project_kit/communication_artifact_gc.py +495 -0
- agentic_project_kit/communication_rule_context.py +437 -0
- agentic_project_kit/communication_state.py +115 -0
- agentic_project_kit/composed_wrapper_invariants.py +97 -0
- agentic_project_kit/composite_short_circuit.py +140 -0
- agentic_project_kit/contract.py +251 -0
- agentic_project_kit/control_state.py +83 -0
- agentic_project_kit/doc_currency_audit.py +536 -0
- agentic_project_kit/doc_lifecycle.py +1179 -0
- agentic_project_kit/doc_lifecycle_signals.py +446 -0
- agentic_project_kit/doc_lifecycle_sweep.py +639 -0
- agentic_project_kit/doc_mesh.py +491 -0
- agentic_project_kit/doctor.py +379 -0
- agentic_project_kit/documentation_registry.py +1122 -0
- agentic_project_kit/documentation_system_audit.py +310 -0
- agentic_project_kit/dpa_current_handoff_lifecycle.py +549 -0
- agentic_project_kit/dpa_dp2_decision_readiness.py +362 -0
- agentic_project_kit/dpa_dp3_dp4_adjudication.py +604 -0
- agentic_project_kit/dpa_dp5_block_new_gate.py +294 -0
- agentic_project_kit/dpa_dp5_stage_adoption.py +615 -0
- agentic_project_kit/dpa_dp5_strict_gate.py +187 -0
- agentic_project_kit/dpa_final_closeout.py +627 -0
- agentic_project_kit/dpa_fixture_evidence.py +680 -0
- agentic_project_kit/dpa_maintainer_record_check.py +711 -0
- agentic_project_kit/dpa_post_dp2_scope_assessment.py +734 -0
- agentic_project_kit/dpa_probe_002_readiness.py +509 -0
- agentic_project_kit/dpa_probe_003_readiness.py +417 -0
- agentic_project_kit/dpa_probe_004_readiness.py +437 -0
- agentic_project_kit/dpa_readiness.py +505 -0
- agentic_project_kit/dpa_readonly_probe_execution.py +500 -0
- agentic_project_kit/dpa_registry_contracts.py +312 -0
- agentic_project_kit/dpa_renderer_readiness.py +407 -0
- agentic_project_kit/dpa_repo_adoption_assessment.py +574 -0
- agentic_project_kit/dpa_stable_readiness.py +743 -0
- agentic_project_kit/dpa_successor_projection.py +168 -0
- agentic_project_kit/dpa_workspace_init_projection.py +93 -0
- agentic_project_kit/dpa_wrt_ch001_evidence.py +327 -0
- agentic_project_kit/entrypoint_slice_runner.py +153 -0
- agentic_project_kit/evidence_clean.py +152 -0
- agentic_project_kit/evidence_commit_paths.py +214 -0
- agentic_project_kit/evidence_finalize_log.py +204 -0
- agentic_project_kit/evidence_guard.py +114 -0
- agentic_project_kit/evidence_inspector.py +507 -0
- agentic_project_kit/evidence_state_contract.py +99 -0
- agentic_project_kit/execution_mode_state.py +148 -0
- agentic_project_kit/final_summary_contract.py +85 -0
- agentic_project_kit/finalize_guard.py +139 -0
- agentic_project_kit/gatekeeper_core.py +232 -0
- agentic_project_kit/github.py +25 -0
- agentic_project_kit/governance.py +59 -0
- agentic_project_kit/gui_action_execution.py +105 -0
- agentic_project_kit/gui_action_renderer.py +81 -0
- agentic_project_kit/gui_action_run_intent.py +98 -0
- agentic_project_kit/gui_action_views.py +195 -0
- agentic_project_kit/gui_activity_log.py +76 -0
- agentic_project_kit/gui_button_catalog.py +605 -0
- agentic_project_kit/gui_cockpit.py +365 -0
- agentic_project_kit/gui_cockpit_actions.py +695 -0
- agentic_project_kit/gui_cockpit_common.py +172 -0
- agentic_project_kit/gui_cockpit_header.py +501 -0
- agentic_project_kit/gui_cockpit_sidebar.py +387 -0
- agentic_project_kit/gui_cockpit_task.py +748 -0
- agentic_project_kit/gui_command_projection.py +355 -0
- agentic_project_kit/gui_communication_modes.py +130 -0
- agentic_project_kit/gui_dry_run.py +219 -0
- agentic_project_kit/gui_gatekeeper_status.py +259 -0
- agentic_project_kit/gui_layout_plan.py +148 -0
- agentic_project_kit/gui_open_folder.py +18 -0
- agentic_project_kit/gui_output_status_panel.py +61 -0
- agentic_project_kit/gui_panel_state.py +47 -0
- agentic_project_kit/gui_presenter.py +62 -0
- agentic_project_kit/gui_readiness_gate.py +206 -0
- agentic_project_kit/gui_release_flow.py +59 -0
- agentic_project_kit/gui_task_editor.py +1077 -0
- agentic_project_kit/gui_tk_widgets.py +177 -0
- agentic_project_kit/gui_tkinter_renderer.py +87 -0
- agentic_project_kit/gui_tkinter_shell.py +1176 -0
- agentic_project_kit/gui_transfer_contract.py +28 -0
- agentic_project_kit/gui_viewmodel.py +698 -0
- agentic_project_kit/gui_window_guard.py +46 -0
- agentic_project_kit/handoff_freshness.py +390 -0
- agentic_project_kit/handoff_prompt.py +180 -0
- agentic_project_kit/handoff_state.py +254 -0
- agentic_project_kit/instruction_lint.py +455 -0
- agentic_project_kit/llm_context_carriers.py +66 -0
- agentic_project_kit/llm_execution_context.py +285 -0
- agentic_project_kit/local_command_stack.py +133 -0
- agentic_project_kit/local_feature_gate.py +82 -0
- agentic_project_kit/local_garbage_collector.py +269 -0
- agentic_project_kit/models.py +18 -0
- agentic_project_kit/mutation_lock_audit.py +421 -0
- agentic_project_kit/next_turn_evidence.py +253 -0
- agentic_project_kit/next_turn_merge_if_green.py +459 -0
- agentic_project_kit/next_turn_pr_status.py +255 -0
- agentic_project_kit/next_turn_result.py +183 -0
- agentic_project_kit/next_turn_runner.py +159 -0
- agentic_project_kit/next_turn_slot.py +107 -0
- agentic_project_kit/next_turn_status.py +274 -0
- agentic_project_kit/ns_legacy_reference_audit.py +258 -0
- agentic_project_kit/ns_slice_runner.py +16 -0
- agentic_project_kit/operational_handoff_projection.py +124 -0
- agentic_project_kit/output_contract.py +80 -0
- agentic_project_kit/output_repair.py +61 -0
- agentic_project_kit/pass_already_done.py +216 -0
- agentic_project_kit/patch_artifact_preflight.py +147 -0
- agentic_project_kit/patch_cycle_workflow.py +440 -0
- agentic_project_kit/patch_failure_discipline_audit.py +223 -0
- agentic_project_kit/patch_preflight.py +215 -0
- agentic_project_kit/path_literal_audit.py +612 -0
- agentic_project_kit/patterns.py +99 -0
- agentic_project_kit/planning_docs_consolidation_audit.py +313 -0
- agentic_project_kit/portability_shell_gate.py +40 -0
- agentic_project_kit/post_merge_handoff_refresh.py +56 -0
- agentic_project_kit/post_release.py +252 -0
- agentic_project_kit/post_release_closeout.py +616 -0
- agentic_project_kit/pr_cleanup.py +172 -0
- agentic_project_kit/pr_closeout.py +50 -0
- agentic_project_kit/pr_closeout_complete.py +344 -0
- agentic_project_kit/pr_create_or_skip.py +71 -0
- agentic_project_kit/pr_hygiene.py +210 -0
- agentic_project_kit/program_redundancy_audit.py +251 -0
- agentic_project_kit/project_direction.py +840 -0
- agentic_project_kit/protected_change_planner.py +194 -0
- agentic_project_kit/release.py +401 -0
- agentic_project_kit/release_doi_safety.py +24 -0
- agentic_project_kit/release_gate_core.py +174 -0
- agentic_project_kit/release_metadata_authority_gate.py +328 -0
- agentic_project_kit/release_metadata_prep.py +43 -0
- agentic_project_kit/release_notes.py +823 -0
- agentic_project_kit/release_prep_core.py +204 -0
- agentic_project_kit/release_prepare.py +369 -0
- agentic_project_kit/release_process_guardrails.py +247 -0
- agentic_project_kit/release_publish_core.py +114 -0
- agentic_project_kit/release_publish_orchestration.py +469 -0
- agentic_project_kit/release_state.py +552 -0
- agentic_project_kit/release_verify_core.py +175 -0
- agentic_project_kit/remote_branch_hygiene.py +223 -0
- agentic_project_kit/remote_next.py +89 -0
- agentic_project_kit/remote_next_closeout.py +101 -0
- agentic_project_kit/remote_next_order_contract.py +391 -0
- agentic_project_kit/removed_ns_commands.py +458 -0
- agentic_project_kit/removed_source_audit.py +225 -0
- agentic_project_kit/rendering.py +13 -0
- agentic_project_kit/repair_report.py +79 -0
- agentic_project_kit/repo_identity.py +59 -0
- agentic_project_kit/result_report_classifier.py +57 -0
- agentic_project_kit/rule_ack.py +105 -0
- agentic_project_kit/rule_preservation.py +58 -0
- agentic_project_kit/rule_refresh.py +174 -0
- agentic_project_kit/rule_registry_registration.py +322 -0
- agentic_project_kit/rule_registry_report.py +206 -0
- agentic_project_kit/rule_registry_validator.py +704 -0
- agentic_project_kit/rule_snapshot.py +98 -0
- agentic_project_kit/rule_source_validator.py +133 -0
- agentic_project_kit/run_summary.py +68 -0
- agentic_project_kit/run_summary_renderer.py +238 -0
- agentic_project_kit/runtime_validator.py +68 -0
- agentic_project_kit/safe_file_replace.py +35 -0
- agentic_project_kit/safe_patch.py +47 -0
- agentic_project_kit/safe_push.py +218 -0
- agentic_project_kit/safe_remove_diagnostic.py +51 -0
- agentic_project_kit/scaffold.py +69 -0
- agentic_project_kit/site_claims.py +444 -0
- agentic_project_kit/site_generator.py +1140 -0
- agentic_project_kit/slice_gate.py +225 -0
- agentic_project_kit/slice_runner.py +75 -0
- agentic_project_kit/standard_gates_audit_suite.py +167 -0
- agentic_project_kit/state_freshness.py +135 -0
- agentic_project_kit/status_current_state_audit.py +762 -0
- agentic_project_kit/successor_handoff_package.py +1763 -0
- agentic_project_kit/templates.py +292 -0
- agentic_project_kit/terminal_block_guard.py +166 -0
- agentic_project_kit/terminal_logging.py +275 -0
- agentic_project_kit/todo.py +97 -0
- agentic_project_kit/transfer_closeout.py +136 -0
- agentic_project_kit/transfer_continue.py +215 -0
- agentic_project_kit/transfer_local_runner.py +101 -0
- agentic_project_kit/transfer_operation_monitor.py +236 -0
- agentic_project_kit/transfer_post_merge_lifecycle.py +356 -0
- agentic_project_kit/transfer_post_merge_settle.py +383 -0
- agentic_project_kit/transfer_pr_actions.py +164 -0
- agentic_project_kit/transfer_remote_next.py +959 -0
- agentic_project_kit/transfer_repo_actions.py +2253 -0
- agentic_project_kit/transfer_runner.py +347 -0
- agentic_project_kit/transfer_safety_context.py +291 -0
- agentic_project_kit/transfer_state.py +483 -0
- agentic_project_kit/transfer_uplink.py +471 -0
- agentic_project_kit/transfer_workflow_next.py +148 -0
- agentic_project_kit/typed_work_order_evidence.py +58 -0
- agentic_project_kit/typed_work_order_queue.py +166 -0
- agentic_project_kit/typed_work_order_runner.py +168 -0
- agentic_project_kit/work_cycle.py +317 -0
- agentic_project_kit/work_discard_changes.py +129 -0
- agentic_project_kit/work_order_patch.py +199 -0
- agentic_project_kit/work_order_runner.py +120 -0
- agentic_project_kit/work_order_uploader.py +220 -0
- agentic_project_kit/work_order_validator.py +185 -0
- agentic_project_kit/work_orders.py +246 -0
- agentic_project_kit/workflow_guard.py +262 -0
- agentic_project_kit/workflow_summary_runner.py +124 -0
- agentic_project_kit/workspace.py +665 -0
- agentic_project_kit/workspace_adopt.py +471 -0
- agentic_project_kit/workspace_dpa_intake.py +406 -0
- agentic_project_kit/workspace_init.py +487 -0
- agentic_project_kit/workspace_lock.py +129 -0
- agentic_project_kit/workspace_remove.py +381 -0
- agentic_project_kit/workspace_upgrade.py +324 -0
- agentic_project_kit/wrapper_live_status.py +104 -0
- agentic_project_kit-1.0.1.dist-info/METADATA +724 -0
- agentic_project_kit-1.0.1.dist-info/RECORD +310 -0
- agentic_project_kit-1.0.1.dist-info/WHEEL +4 -0
- agentic_project_kit-1.0.1.dist-info/entry_points.txt +3 -0
- agentic_project_kit-1.0.1.dist-info/licenses/LICENSE +10 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""Deterministic GUI gatekeeper status model.
|
|
2
|
+
|
|
3
|
+
This module is intentionally read-only. It builds a stable status snapshot that
|
|
4
|
+
GUI surfaces can render before allowing action execution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import subprocess
|
|
12
|
+
from typing import Sequence
|
|
13
|
+
import yaml
|
|
14
|
+
|
|
15
|
+
from agentic_project_kit.action_registry import ACTIONS, SafetyClass
|
|
16
|
+
from agentic_project_kit.communication_rule_context import require_current_communication_context
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class GuiGatekeeperActionStatus:
|
|
21
|
+
action_id: str
|
|
22
|
+
safety_class: str
|
|
23
|
+
mutation_scope: str
|
|
24
|
+
enabled: bool
|
|
25
|
+
reason: str
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class GuiGatekeeperStatus:
|
|
30
|
+
branch: str
|
|
31
|
+
git_dirty: bool
|
|
32
|
+
workflow_state: str
|
|
33
|
+
current_work_present: bool
|
|
34
|
+
current_work_state: str | None
|
|
35
|
+
ready_for_read_only_actions: bool
|
|
36
|
+
ready_for_mutating_actions: bool
|
|
37
|
+
action_statuses: tuple[GuiGatekeeperActionStatus, ...]
|
|
38
|
+
blockers: tuple[str, ...]
|
|
39
|
+
communication_context_fresh: bool = True
|
|
40
|
+
communication_context_reason: str = "communication_context_current"
|
|
41
|
+
required_next_reply: str | None = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def build_gui_gatekeeper_status(
|
|
45
|
+
project_root: Path | str = ".",
|
|
46
|
+
*,
|
|
47
|
+
actions: Sequence[object] | None = None,
|
|
48
|
+
) -> GuiGatekeeperStatus:
|
|
49
|
+
"""Build a deterministic, read-only GUI gatekeeper snapshot."""
|
|
50
|
+
|
|
51
|
+
root = Path(project_root)
|
|
52
|
+
branch = _git_branch(root)
|
|
53
|
+
git_dirty = _git_dirty(root)
|
|
54
|
+
workflow_state = _read_text(root / ".agentic" / "workflow_state", "missing").strip() or "missing"
|
|
55
|
+
current_work_path = root / ".agentic" / "current_work.yaml"
|
|
56
|
+
current_work_present = current_work_path.exists()
|
|
57
|
+
current_work_state = _read_current_work_state(current_work_path) if current_work_present else None
|
|
58
|
+
communication_context = require_current_communication_context(root)
|
|
59
|
+
communication_context_fresh = communication_context.result_status == "PASS"
|
|
60
|
+
|
|
61
|
+
blockers: list[str] = []
|
|
62
|
+
if branch == "unknown":
|
|
63
|
+
blockers.append("git branch could not be determined")
|
|
64
|
+
if git_dirty:
|
|
65
|
+
blockers.append("working tree is dirty")
|
|
66
|
+
if workflow_state not in {"IDLE", "READY"}:
|
|
67
|
+
blockers.append(f"workflow_state is {workflow_state}")
|
|
68
|
+
if not communication_context_fresh:
|
|
69
|
+
blockers.append(communication_context.reason)
|
|
70
|
+
|
|
71
|
+
ready_for_read_only_actions = not git_dirty
|
|
72
|
+
ready_for_mutating_actions = (
|
|
73
|
+
not git_dirty
|
|
74
|
+
and branch != "unknown"
|
|
75
|
+
and workflow_state in {"IDLE", "READY"}
|
|
76
|
+
and communication_context_fresh
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
action_statuses = tuple(
|
|
80
|
+
classify_gui_gatekeeper_action(
|
|
81
|
+
action,
|
|
82
|
+
git_dirty=git_dirty,
|
|
83
|
+
local_only_allowed=ready_for_mutating_actions,
|
|
84
|
+
)
|
|
85
|
+
for action in (actions if actions is not None else ACTIONS)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return GuiGatekeeperStatus(
|
|
89
|
+
branch=branch,
|
|
90
|
+
git_dirty=git_dirty,
|
|
91
|
+
workflow_state=workflow_state,
|
|
92
|
+
current_work_present=current_work_present,
|
|
93
|
+
current_work_state=current_work_state,
|
|
94
|
+
ready_for_read_only_actions=ready_for_read_only_actions,
|
|
95
|
+
ready_for_mutating_actions=ready_for_mutating_actions,
|
|
96
|
+
action_statuses=action_statuses,
|
|
97
|
+
blockers=tuple(blockers),
|
|
98
|
+
communication_context_fresh=communication_context_fresh,
|
|
99
|
+
communication_context_reason=communication_context.reason,
|
|
100
|
+
required_next_reply=communication_context.required_next_reply,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def classify_gui_gatekeeper_action(
|
|
105
|
+
action: object,
|
|
106
|
+
*,
|
|
107
|
+
git_dirty: bool,
|
|
108
|
+
local_only_allowed: bool = False,
|
|
109
|
+
) -> GuiGatekeeperActionStatus:
|
|
110
|
+
action_id = str(getattr(action, "name", getattr(action, "action_id", "<unknown>")))
|
|
111
|
+
safety_class = _safety_value(getattr(action, "safety_class", getattr(action, "safety", "unknown")))
|
|
112
|
+
mutation_scope = str(getattr(action, "mutation_scope", "unknown"))
|
|
113
|
+
|
|
114
|
+
if safety_class == SafetyClass.READ_ONLY.value and not git_dirty:
|
|
115
|
+
return GuiGatekeeperActionStatus(
|
|
116
|
+
action_id=action_id,
|
|
117
|
+
safety_class=safety_class,
|
|
118
|
+
mutation_scope=mutation_scope,
|
|
119
|
+
enabled=True,
|
|
120
|
+
reason="read-only action allowed in clean GUI gatekeeper state",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if safety_class == SafetyClass.READ_ONLY.value and git_dirty:
|
|
124
|
+
return GuiGatekeeperActionStatus(
|
|
125
|
+
action_id=action_id,
|
|
126
|
+
safety_class=safety_class,
|
|
127
|
+
mutation_scope=mutation_scope,
|
|
128
|
+
enabled=False,
|
|
129
|
+
reason="read-only action blocked because working tree is dirty",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if safety_class == SafetyClass.LOCAL_ONLY.value and local_only_allowed:
|
|
133
|
+
return GuiGatekeeperActionStatus(
|
|
134
|
+
action_id=action_id,
|
|
135
|
+
safety_class=safety_class,
|
|
136
|
+
mutation_scope=mutation_scope,
|
|
137
|
+
enabled=True,
|
|
138
|
+
reason="local-only action allowed in clean GUI gatekeeper state",
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if safety_class == SafetyClass.LOCAL_ONLY.value:
|
|
142
|
+
return GuiGatekeeperActionStatus(
|
|
143
|
+
action_id=action_id,
|
|
144
|
+
safety_class=safety_class,
|
|
145
|
+
mutation_scope=mutation_scope,
|
|
146
|
+
enabled=False,
|
|
147
|
+
reason="local-only action blocked because GUI gatekeeper is not clean",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return GuiGatekeeperActionStatus(
|
|
151
|
+
action_id=action_id,
|
|
152
|
+
safety_class=safety_class,
|
|
153
|
+
mutation_scope=mutation_scope,
|
|
154
|
+
enabled=False,
|
|
155
|
+
reason="GUI gatekeeper blocks remote mutation actions",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def render_gui_gatekeeper_status(status: GuiGatekeeperStatus) -> str:
|
|
160
|
+
lines = [
|
|
161
|
+
"GUI_GATEKEEPER_STATUS",
|
|
162
|
+
f"branch={status.branch}",
|
|
163
|
+
f"git_dirty={str(status.git_dirty).lower()}",
|
|
164
|
+
f"workflow_state={status.workflow_state}",
|
|
165
|
+
f"current_work_present={str(status.current_work_present).lower()}",
|
|
166
|
+
f"current_work_state={status.current_work_state or '<none>'}",
|
|
167
|
+
f"ready_for_read_only_actions={str(status.ready_for_read_only_actions).lower()}",
|
|
168
|
+
f"ready_for_mutating_actions={str(status.ready_for_mutating_actions).lower()}",
|
|
169
|
+
f"communication_context_fresh={str(status.communication_context_fresh).lower()}",
|
|
170
|
+
f"communication_context_reason={status.communication_context_reason}",
|
|
171
|
+
f"required_next_reply={status.required_next_reply or '<none>'}",
|
|
172
|
+
"blockers=" + (",".join(status.blockers) if status.blockers else "<none>"),
|
|
173
|
+
]
|
|
174
|
+
for action in status.action_statuses:
|
|
175
|
+
lines.append(
|
|
176
|
+
"action="
|
|
177
|
+
+ action.action_id
|
|
178
|
+
+ ";safety="
|
|
179
|
+
+ action.safety_class
|
|
180
|
+
+ ";enabled="
|
|
181
|
+
+ str(action.enabled).lower()
|
|
182
|
+
+ ";reason="
|
|
183
|
+
+ action.reason
|
|
184
|
+
)
|
|
185
|
+
return "\n".join(lines)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def gui_gatekeeper_status_as_json_data(status: GuiGatekeeperStatus) -> dict[str, object]:
|
|
189
|
+
return {
|
|
190
|
+
"schema_version": 1,
|
|
191
|
+
"branch": status.branch,
|
|
192
|
+
"git_dirty": status.git_dirty,
|
|
193
|
+
"workflow_state": status.workflow_state,
|
|
194
|
+
"current_work_present": status.current_work_present,
|
|
195
|
+
"current_work_state": status.current_work_state,
|
|
196
|
+
"ready_for_read_only_actions": status.ready_for_read_only_actions,
|
|
197
|
+
"ready_for_mutating_actions": status.ready_for_mutating_actions,
|
|
198
|
+
"communication_context_fresh": status.communication_context_fresh,
|
|
199
|
+
"communication_context_reason": status.communication_context_reason,
|
|
200
|
+
"required_next_reply": status.required_next_reply,
|
|
201
|
+
"blockers": list(status.blockers),
|
|
202
|
+
"actions": [
|
|
203
|
+
{
|
|
204
|
+
"action_id": action.action_id,
|
|
205
|
+
"safety_class": action.safety_class,
|
|
206
|
+
"mutation_scope": action.mutation_scope,
|
|
207
|
+
"enabled": action.enabled,
|
|
208
|
+
"reason": action.reason,
|
|
209
|
+
}
|
|
210
|
+
for action in status.action_statuses
|
|
211
|
+
],
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _safety_value(value: object) -> str:
|
|
216
|
+
raw = getattr(value, "value", value)
|
|
217
|
+
return str(raw).strip().lower().replace("_", "-")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _git_branch(root: Path) -> str:
|
|
221
|
+
try:
|
|
222
|
+
return subprocess.check_output(
|
|
223
|
+
["git", "-C", str(root), "branch", "--show-current"],
|
|
224
|
+
text=True,
|
|
225
|
+
stderr=subprocess.DEVNULL,
|
|
226
|
+
).strip() or "unknown"
|
|
227
|
+
except (OSError, subprocess.CalledProcessError):
|
|
228
|
+
return "unknown"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _git_dirty(root: Path) -> bool:
|
|
232
|
+
try:
|
|
233
|
+
return bool(
|
|
234
|
+
subprocess.check_output(
|
|
235
|
+
["git", "-C", str(root), "status", "--porcelain"],
|
|
236
|
+
text=True,
|
|
237
|
+
stderr=subprocess.DEVNULL,
|
|
238
|
+
).strip()
|
|
239
|
+
)
|
|
240
|
+
except (OSError, subprocess.CalledProcessError):
|
|
241
|
+
return True
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _read_text(path: Path, default: str) -> str:
|
|
245
|
+
try:
|
|
246
|
+
return path.read_text(encoding="utf-8")
|
|
247
|
+
except OSError:
|
|
248
|
+
return default
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _read_current_work_state(path: Path) -> str | None:
|
|
252
|
+
try:
|
|
253
|
+
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
254
|
+
except (OSError, yaml.YAMLError):
|
|
255
|
+
return None
|
|
256
|
+
if not isinstance(data, dict):
|
|
257
|
+
return None
|
|
258
|
+
state = data.get("state")
|
|
259
|
+
return str(state) if state is not None else None
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from agentic_project_kit.gui_tkinter_shell import TkinterShellSpec, build_tkinter_shell_spec
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class LayoutNode:
|
|
10
|
+
node_id: str
|
|
11
|
+
kind: str
|
|
12
|
+
label: str
|
|
13
|
+
parent: str
|
|
14
|
+
order: int
|
|
15
|
+
enabled: bool = True
|
|
16
|
+
tooltip: str = ""
|
|
17
|
+
command_id: str = ""
|
|
18
|
+
safety_class: str = ""
|
|
19
|
+
icon_id: str = ""
|
|
20
|
+
category: str = ""
|
|
21
|
+
implementation_state: str = ""
|
|
22
|
+
disabled_reason: str = ""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class LayoutPlan:
|
|
27
|
+
title: str
|
|
28
|
+
geometry: str
|
|
29
|
+
nodes: tuple[LayoutNode, ...]
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def node_count(self) -> int:
|
|
33
|
+
return len(self.nodes)
|
|
34
|
+
|
|
35
|
+
def nodes_by_kind(self, kind: str) -> tuple[LayoutNode, ...]:
|
|
36
|
+
return tuple(node for node in self.nodes if node.kind == kind)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _append(
|
|
40
|
+
nodes: list[LayoutNode],
|
|
41
|
+
node_id: str,
|
|
42
|
+
kind: str,
|
|
43
|
+
label: str,
|
|
44
|
+
parent: str,
|
|
45
|
+
enabled: bool = True,
|
|
46
|
+
tooltip: str = "",
|
|
47
|
+
command_id: str = "",
|
|
48
|
+
safety_class: str = "",
|
|
49
|
+
icon_id: str = "",
|
|
50
|
+
category: str = "",
|
|
51
|
+
implementation_state: str = "",
|
|
52
|
+
disabled_reason: str = "",
|
|
53
|
+
) -> None:
|
|
54
|
+
nodes.append(
|
|
55
|
+
LayoutNode(
|
|
56
|
+
node_id=node_id,
|
|
57
|
+
kind=kind,
|
|
58
|
+
label=label,
|
|
59
|
+
parent=parent,
|
|
60
|
+
order=len(nodes),
|
|
61
|
+
enabled=enabled,
|
|
62
|
+
tooltip=tooltip,
|
|
63
|
+
command_id=command_id,
|
|
64
|
+
safety_class=safety_class,
|
|
65
|
+
icon_id=icon_id,
|
|
66
|
+
category=category,
|
|
67
|
+
implementation_state=implementation_state,
|
|
68
|
+
disabled_reason=disabled_reason,
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def build_layout_plan(spec: TkinterShellSpec | None = None) -> LayoutPlan:
|
|
74
|
+
shell = build_tkinter_shell_spec() if spec is None else spec
|
|
75
|
+
nodes: list[LayoutNode] = []
|
|
76
|
+
_append(nodes, "root", "window", shell.title, "")
|
|
77
|
+
_append(nodes, "menu-bar", "menu_bar", "Menu", "root")
|
|
78
|
+
for menu in shell.design.menu_bar:
|
|
79
|
+
menu_id = "menu-" + menu.label.lower().replace(" ", "-")
|
|
80
|
+
_append(nodes, menu_id, "menu", menu.label, "menu-bar")
|
|
81
|
+
for item in menu.items:
|
|
82
|
+
_append(
|
|
83
|
+
nodes,
|
|
84
|
+
menu_id + "-" + item.command_id,
|
|
85
|
+
"menu_item",
|
|
86
|
+
item.label,
|
|
87
|
+
menu_id,
|
|
88
|
+
item.enabled,
|
|
89
|
+
item.tooltip,
|
|
90
|
+
item.command_id,
|
|
91
|
+
)
|
|
92
|
+
_append(nodes, "toolbar", "toolbar", "Toolbar", "root")
|
|
93
|
+
for button in shell.design.toolbar:
|
|
94
|
+
_append(
|
|
95
|
+
nodes,
|
|
96
|
+
"toolbar-" + button.command_id,
|
|
97
|
+
"toolbar_button",
|
|
98
|
+
button.label,
|
|
99
|
+
"toolbar",
|
|
100
|
+
button.enabled,
|
|
101
|
+
button.tooltip,
|
|
102
|
+
button.command_id,
|
|
103
|
+
button.safety_class,
|
|
104
|
+
button.icon_id,
|
|
105
|
+
button.category,
|
|
106
|
+
button.implementation_state,
|
|
107
|
+
button.disabled_reason,
|
|
108
|
+
)
|
|
109
|
+
_append(nodes, "main", "main_area", "Main", "root")
|
|
110
|
+
_append(nodes, "actions", "action_panel", "Actions", "main")
|
|
111
|
+
for button in shell.design.action_buttons:
|
|
112
|
+
_append(
|
|
113
|
+
nodes,
|
|
114
|
+
"action-" + button.command_id,
|
|
115
|
+
"action_button",
|
|
116
|
+
button.label,
|
|
117
|
+
"actions",
|
|
118
|
+
button.enabled,
|
|
119
|
+
button.tooltip,
|
|
120
|
+
button.command_id,
|
|
121
|
+
button.safety_class,
|
|
122
|
+
button.icon_id,
|
|
123
|
+
button.category,
|
|
124
|
+
button.implementation_state,
|
|
125
|
+
button.disabled_reason,
|
|
126
|
+
)
|
|
127
|
+
_append(nodes, "details", "details_panel", "Details / Parameters", "main")
|
|
128
|
+
_append(nodes, "output", "output_panel", "Output / Log", "root")
|
|
129
|
+
_append(nodes, "summary", "summary_bar", "Last Summary", "root")
|
|
130
|
+
return LayoutPlan(title=shell.title, geometry="1000x650", nodes=tuple(nodes))
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def render_layout_plan(plan: LayoutPlan) -> str:
|
|
134
|
+
lines = [
|
|
135
|
+
"GUI LAYOUT PLAN",
|
|
136
|
+
f"title={plan.title}",
|
|
137
|
+
f"geometry={plan.geometry}",
|
|
138
|
+
f"node_count={plan.node_count}",
|
|
139
|
+
]
|
|
140
|
+
for kind in ("menu", "toolbar_button", "action_button", "output_panel", "summary_bar"):
|
|
141
|
+
lines.append(f"{kind}_count={len(plan.nodes_by_kind(kind))}")
|
|
142
|
+
categories = sorted(
|
|
143
|
+
{node.category for node in plan.nodes_by_kind("action_button") if node.category}
|
|
144
|
+
)
|
|
145
|
+
lines.append("action_categories=" + (",".join(categories) if categories else "NONE"))
|
|
146
|
+
disabled = [node.command_id for node in plan.nodes if not node.enabled]
|
|
147
|
+
lines.append("disabled_commands=" + (",".join(disabled) if disabled else "NONE"))
|
|
148
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def open_folder_in_file_manager(path: Path) -> None:
|
|
10
|
+
target = Path(path)
|
|
11
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
12
|
+
if sys.platform == "darwin":
|
|
13
|
+
subprocess.run(["open", str(target)], check=False)
|
|
14
|
+
return
|
|
15
|
+
if sys.platform.startswith("win"):
|
|
16
|
+
os.startfile(str(target)) # type: ignore[attr-defined] # noqa: S606
|
|
17
|
+
return
|
|
18
|
+
subprocess.run(["xdg-open", str(target)], check=False)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class GuiOutputStatusPanel:
|
|
8
|
+
status: str
|
|
9
|
+
branch: str
|
|
10
|
+
dirty: bool
|
|
11
|
+
latest_output: str
|
|
12
|
+
terminal_log: str
|
|
13
|
+
terminal_log_remote: str
|
|
14
|
+
terminal_log_local: str
|
|
15
|
+
evidence_state: str
|
|
16
|
+
summary: str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def build_output_status_panel(
|
|
20
|
+
*,
|
|
21
|
+
branch: str = "unknown",
|
|
22
|
+
dirty: bool = False,
|
|
23
|
+
latest_output: str = "",
|
|
24
|
+
terminal_log: str = "NONE",
|
|
25
|
+
terminal_log_remote: str = "NONE",
|
|
26
|
+
terminal_log_local: str = "NONE",
|
|
27
|
+
evidence_state: str = "local_only",
|
|
28
|
+
summary: str = "",
|
|
29
|
+
) -> GuiOutputStatusPanel:
|
|
30
|
+
status = "dirty" if dirty else "clean"
|
|
31
|
+
return GuiOutputStatusPanel(
|
|
32
|
+
status=status,
|
|
33
|
+
branch=branch,
|
|
34
|
+
dirty=dirty,
|
|
35
|
+
latest_output=latest_output or "No output captured yet.",
|
|
36
|
+
terminal_log=terminal_log,
|
|
37
|
+
terminal_log_remote=terminal_log_remote,
|
|
38
|
+
terminal_log_local=terminal_log_local,
|
|
39
|
+
evidence_state=evidence_state,
|
|
40
|
+
summary=summary or "No summary captured yet.",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def render_output_status_panel(panel: GuiOutputStatusPanel) -> str:
|
|
45
|
+
lines = [
|
|
46
|
+
"GUI OUTPUT STATUS PANEL",
|
|
47
|
+
f"status={panel.status}",
|
|
48
|
+
f"branch={panel.branch}",
|
|
49
|
+
f"dirty={str(panel.dirty).lower()}",
|
|
50
|
+
f"terminal_log={panel.terminal_log}",
|
|
51
|
+
f"terminal_log_remote={panel.terminal_log_remote}",
|
|
52
|
+
f"terminal_log_local={panel.terminal_log_local}",
|
|
53
|
+
f"evidence_state={panel.evidence_state}",
|
|
54
|
+
"latest_output_begin",
|
|
55
|
+
panel.latest_output,
|
|
56
|
+
"latest_output_end",
|
|
57
|
+
"summary_begin",
|
|
58
|
+
panel.summary,
|
|
59
|
+
"summary_end",
|
|
60
|
+
]
|
|
61
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from agentic_project_kit.workspace import LEGACY_DEFAULTS, load_workspace
|
|
9
|
+
|
|
10
|
+
PANEL_STATE_FILE = "gui-panel-state.json"
|
|
11
|
+
PANEL_STATE_RELATIVE_PATH = Path(LEGACY_DEFAULTS.tmp_root) / PANEL_STATE_FILE
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def panel_state_path(root: Path = Path(".")) -> Path:
|
|
15
|
+
return load_workspace(root).gui_panel_state_path()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _coerce_state(data: Any) -> dict[str, bool]:
|
|
19
|
+
if isinstance(data, dict) and isinstance(data.get("expanded_groups"), dict):
|
|
20
|
+
data = data["expanded_groups"]
|
|
21
|
+
if not isinstance(data, dict):
|
|
22
|
+
return {}
|
|
23
|
+
return {str(key): bool(value) for key, value in data.items() if isinstance(key, str)}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def read_panel_state(root: Path = Path(".")) -> dict[str, bool]:
|
|
27
|
+
path = panel_state_path(root)
|
|
28
|
+
try:
|
|
29
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
30
|
+
except (FileNotFoundError, OSError, json.JSONDecodeError):
|
|
31
|
+
return {}
|
|
32
|
+
return _coerce_state(data)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def write_panel_state(root: Path = Path("."), state: dict[str, bool] | None = None) -> Path:
|
|
36
|
+
path = panel_state_path(root)
|
|
37
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
workspace = load_workspace(root)
|
|
39
|
+
payload = {
|
|
40
|
+
"schema_version": 1,
|
|
41
|
+
"kind": "gui_panel_state",
|
|
42
|
+
"state_path": workspace.path_text(path),
|
|
43
|
+
"updated_at_utc": datetime.now(timezone.utc).isoformat(),
|
|
44
|
+
"expanded_groups": dict(sorted((state or {}).items())),
|
|
45
|
+
}
|
|
46
|
+
path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
47
|
+
return path
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from agentic_project_kit.gui_action_renderer import (
|
|
7
|
+
render_basic_cockpit_view_model,
|
|
8
|
+
render_controller_view_model,
|
|
9
|
+
)
|
|
10
|
+
from agentic_project_kit.gui_gatekeeper_status import GuiGatekeeperStatus
|
|
11
|
+
from agentic_project_kit.gui_viewmodel import (
|
|
12
|
+
build_basic_cockpit_view_model,
|
|
13
|
+
build_gui_controller_view_model,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class GuiPresenterResult:
|
|
19
|
+
ok: bool
|
|
20
|
+
action_count: int
|
|
21
|
+
rendered: str
|
|
22
|
+
message: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_no_window_presenter_result(
|
|
26
|
+
actions: Iterable[object],
|
|
27
|
+
*,
|
|
28
|
+
title: str = "agentic-project-kit Cockpit",
|
|
29
|
+
status: str = "dry-run-ready",
|
|
30
|
+
destructive_actions_enabled: bool = False,
|
|
31
|
+
) -> GuiPresenterResult:
|
|
32
|
+
view_model = build_gui_controller_view_model(
|
|
33
|
+
actions,
|
|
34
|
+
title=title,
|
|
35
|
+
status=status,
|
|
36
|
+
destructive_actions_enabled=destructive_actions_enabled,
|
|
37
|
+
)
|
|
38
|
+
rendered = render_controller_view_model(view_model)
|
|
39
|
+
return GuiPresenterResult(
|
|
40
|
+
ok=True,
|
|
41
|
+
action_count=view_model.action_count,
|
|
42
|
+
rendered=rendered,
|
|
43
|
+
message="GUI no-window presenter passed.",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def build_basic_no_window_presenter_result(
|
|
48
|
+
*,
|
|
49
|
+
gatekeeper_status: GuiGatekeeperStatus | None = None,
|
|
50
|
+
communication_mode: str = "file_transfer",
|
|
51
|
+
) -> GuiPresenterResult:
|
|
52
|
+
view_model = build_basic_cockpit_view_model(
|
|
53
|
+
gatekeeper_status=gatekeeper_status,
|
|
54
|
+
communication_mode=communication_mode,
|
|
55
|
+
)
|
|
56
|
+
rendered = render_basic_cockpit_view_model(view_model)
|
|
57
|
+
return GuiPresenterResult(
|
|
58
|
+
ok=True,
|
|
59
|
+
action_count=view_model.button_count,
|
|
60
|
+
rendered=rendered,
|
|
61
|
+
message="Basic cockpit no-window presenter passed.",
|
|
62
|
+
)
|