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.
Files changed (310) hide show
  1. agentic_project_kit/__init__.py +1 -0
  2. agentic_project_kit/absolute_path_portability_audit.py +241 -0
  3. agentic_project_kit/access_level_policy.py +14 -0
  4. agentic_project_kit/access_levels.py +22 -0
  5. agentic_project_kit/action_registry.py +195 -0
  6. agentic_project_kit/action_specs.py +286 -0
  7. agentic_project_kit/agent_command_runner.py +516 -0
  8. agentic_project_kit/audit_test_matrix.py +36 -0
  9. agentic_project_kit/audit_test_matrix_cli.py +43 -0
  10. agentic_project_kit/chat_bootloader.py +257 -0
  11. agentic_project_kit/chat_entrypoint_contract.py +297 -0
  12. agentic_project_kit/checks.py +511 -0
  13. agentic_project_kit/ci_readiness.py +211 -0
  14. agentic_project_kit/cli.py +147 -0
  15. agentic_project_kit/cli_commands/__init__.py +1 -0
  16. agentic_project_kit/cli_commands/absolute_path_audit.py +25 -0
  17. agentic_project_kit/cli_commands/actions.py +21 -0
  18. agentic_project_kit/cli_commands/artifact_gc.py +240 -0
  19. agentic_project_kit/cli_commands/boot.py +55 -0
  20. agentic_project_kit/cli_commands/chat.py +34 -0
  21. agentic_project_kit/cli_commands/checks.py +305 -0
  22. agentic_project_kit/cli_commands/cockpit.py +103 -0
  23. agentic_project_kit/cli_commands/command_taxonomy.py +25 -0
  24. agentic_project_kit/cli_commands/commands.py +129 -0
  25. agentic_project_kit/cli_commands/dev.py +23 -0
  26. agentic_project_kit/cli_commands/direction.py +92 -0
  27. agentic_project_kit/cli_commands/doc_currency_audit.py +25 -0
  28. agentic_project_kit/cli_commands/doc_registry.py +126 -0
  29. agentic_project_kit/cli_commands/docs.py +223 -0
  30. agentic_project_kit/cli_commands/dpa.py +1338 -0
  31. agentic_project_kit/cli_commands/evidence.py +251 -0
  32. agentic_project_kit/cli_commands/github.py +34 -0
  33. agentic_project_kit/cli_commands/governance.py +14 -0
  34. agentic_project_kit/cli_commands/gui.py +23 -0
  35. agentic_project_kit/cli_commands/gui_readiness_gate.py +27 -0
  36. agentic_project_kit/cli_commands/handoff.py +183 -0
  37. agentic_project_kit/cli_commands/human_workflows.py +430 -0
  38. agentic_project_kit/cli_commands/init.py +132 -0
  39. agentic_project_kit/cli_commands/instruction.py +75 -0
  40. agentic_project_kit/cli_commands/mutation_lock_audit.py +24 -0
  41. agentic_project_kit/cli_commands/ns_legacy_audit.py +25 -0
  42. agentic_project_kit/cli_commands/pass_already_done.py +66 -0
  43. agentic_project_kit/cli_commands/patch_failure_discipline_audit.py +26 -0
  44. agentic_project_kit/cli_commands/patch_preflight.py +37 -0
  45. agentic_project_kit/cli_commands/path_literal_audit.py +39 -0
  46. agentic_project_kit/cli_commands/patterns.py +40 -0
  47. agentic_project_kit/cli_commands/planning_docs_audit.py +25 -0
  48. agentic_project_kit/cli_commands/pr.py +133 -0
  49. agentic_project_kit/cli_commands/pr_hygiene.py +27 -0
  50. agentic_project_kit/cli_commands/profiles.py +26 -0
  51. agentic_project_kit/cli_commands/program_redundancy_audit.py +25 -0
  52. agentic_project_kit/cli_commands/project_direction.py +25 -0
  53. agentic_project_kit/cli_commands/release.py +392 -0
  54. agentic_project_kit/cli_commands/release_publish_orchestration.py +36 -0
  55. agentic_project_kit/cli_commands/remote_branch_hygiene.py +152 -0
  56. agentic_project_kit/cli_commands/remote_next.py +57 -0
  57. agentic_project_kit/cli_commands/removed_ns_commands.py +65 -0
  58. agentic_project_kit/cli_commands/rule_registry.py +129 -0
  59. agentic_project_kit/cli_commands/rules.py +222 -0
  60. agentic_project_kit/cli_commands/scaffold.py +46 -0
  61. agentic_project_kit/cli_commands/slice.py +21 -0
  62. agentic_project_kit/cli_commands/standard_gates_audit_suite.py +27 -0
  63. agentic_project_kit/cli_commands/state.py +51 -0
  64. agentic_project_kit/cli_commands/status_current_state_audit.py +30 -0
  65. agentic_project_kit/cli_commands/todo.py +53 -0
  66. agentic_project_kit/cli_commands/transfer.py +19 -0
  67. agentic_project_kit/cli_commands/transfer_context_flow.py +398 -0
  68. agentic_project_kit/cli_commands/transfer_context_helpers.py +398 -0
  69. agentic_project_kit/cli_commands/transfer_diagnostics.py +767 -0
  70. agentic_project_kit/cli_commands/transfer_evidence_flow.py +368 -0
  71. agentic_project_kit/cli_commands/transfer_handoff_flow.py +594 -0
  72. agentic_project_kit/cli_commands/transfer_order_contract.py +74 -0
  73. agentic_project_kit/cli_commands/transfer_post_merge_complete.py +442 -0
  74. agentic_project_kit/cli_commands/transfer_post_merge_settle.py +171 -0
  75. agentic_project_kit/cli_commands/transfer_pr_closeout_complete.py +124 -0
  76. agentic_project_kit/cli_commands/transfer_pr_create_flow.py +743 -0
  77. agentic_project_kit/cli_commands/transfer_pr_merge_flow.py +547 -0
  78. agentic_project_kit/cli_commands/transfer_repo_after_pr.py +160 -0
  79. agentic_project_kit/cli_commands/transfer_repo_pre_pr.py +664 -0
  80. agentic_project_kit/cli_commands/transfer_runner_flow.py +131 -0
  81. agentic_project_kit/cli_commands/transfer_runtime_initial.py +194 -0
  82. agentic_project_kit/cli_commands/transfer_shared.py +274 -0
  83. agentic_project_kit/cli_commands/validation.py +200 -0
  84. agentic_project_kit/cli_commands/version.py +27 -0
  85. agentic_project_kit/cli_commands/work_orders.py +219 -0
  86. agentic_project_kit/cli_commands/workflow.py +471 -0
  87. agentic_project_kit/cli_commands/workflow_guard.py +26 -0
  88. agentic_project_kit/cli_commands/workspace.py +252 -0
  89. agentic_project_kit/cockpit.py +608 -0
  90. agentic_project_kit/cockpit_readiness.py +67 -0
  91. agentic_project_kit/command_authority_audit.py +192 -0
  92. agentic_project_kit/command_inbox_check.py +103 -0
  93. agentic_project_kit/command_manifest.py +591 -0
  94. agentic_project_kit/command_queue_contract.py +161 -0
  95. agentic_project_kit/command_selector.py +245 -0
  96. agentic_project_kit/command_taxonomy.py +264 -0
  97. agentic_project_kit/commit_guard.py +67 -0
  98. agentic_project_kit/communication_artifact_gc.py +495 -0
  99. agentic_project_kit/communication_rule_context.py +437 -0
  100. agentic_project_kit/communication_state.py +115 -0
  101. agentic_project_kit/composed_wrapper_invariants.py +97 -0
  102. agentic_project_kit/composite_short_circuit.py +140 -0
  103. agentic_project_kit/contract.py +251 -0
  104. agentic_project_kit/control_state.py +83 -0
  105. agentic_project_kit/doc_currency_audit.py +536 -0
  106. agentic_project_kit/doc_lifecycle.py +1179 -0
  107. agentic_project_kit/doc_lifecycle_signals.py +446 -0
  108. agentic_project_kit/doc_lifecycle_sweep.py +639 -0
  109. agentic_project_kit/doc_mesh.py +491 -0
  110. agentic_project_kit/doctor.py +379 -0
  111. agentic_project_kit/documentation_registry.py +1122 -0
  112. agentic_project_kit/documentation_system_audit.py +310 -0
  113. agentic_project_kit/dpa_current_handoff_lifecycle.py +549 -0
  114. agentic_project_kit/dpa_dp2_decision_readiness.py +362 -0
  115. agentic_project_kit/dpa_dp3_dp4_adjudication.py +604 -0
  116. agentic_project_kit/dpa_dp5_block_new_gate.py +294 -0
  117. agentic_project_kit/dpa_dp5_stage_adoption.py +615 -0
  118. agentic_project_kit/dpa_dp5_strict_gate.py +187 -0
  119. agentic_project_kit/dpa_final_closeout.py +627 -0
  120. agentic_project_kit/dpa_fixture_evidence.py +680 -0
  121. agentic_project_kit/dpa_maintainer_record_check.py +711 -0
  122. agentic_project_kit/dpa_post_dp2_scope_assessment.py +734 -0
  123. agentic_project_kit/dpa_probe_002_readiness.py +509 -0
  124. agentic_project_kit/dpa_probe_003_readiness.py +417 -0
  125. agentic_project_kit/dpa_probe_004_readiness.py +437 -0
  126. agentic_project_kit/dpa_readiness.py +505 -0
  127. agentic_project_kit/dpa_readonly_probe_execution.py +500 -0
  128. agentic_project_kit/dpa_registry_contracts.py +312 -0
  129. agentic_project_kit/dpa_renderer_readiness.py +407 -0
  130. agentic_project_kit/dpa_repo_adoption_assessment.py +574 -0
  131. agentic_project_kit/dpa_stable_readiness.py +743 -0
  132. agentic_project_kit/dpa_successor_projection.py +168 -0
  133. agentic_project_kit/dpa_workspace_init_projection.py +93 -0
  134. agentic_project_kit/dpa_wrt_ch001_evidence.py +327 -0
  135. agentic_project_kit/entrypoint_slice_runner.py +153 -0
  136. agentic_project_kit/evidence_clean.py +152 -0
  137. agentic_project_kit/evidence_commit_paths.py +214 -0
  138. agentic_project_kit/evidence_finalize_log.py +204 -0
  139. agentic_project_kit/evidence_guard.py +114 -0
  140. agentic_project_kit/evidence_inspector.py +507 -0
  141. agentic_project_kit/evidence_state_contract.py +99 -0
  142. agentic_project_kit/execution_mode_state.py +148 -0
  143. agentic_project_kit/final_summary_contract.py +85 -0
  144. agentic_project_kit/finalize_guard.py +139 -0
  145. agentic_project_kit/gatekeeper_core.py +232 -0
  146. agentic_project_kit/github.py +25 -0
  147. agentic_project_kit/governance.py +59 -0
  148. agentic_project_kit/gui_action_execution.py +105 -0
  149. agentic_project_kit/gui_action_renderer.py +81 -0
  150. agentic_project_kit/gui_action_run_intent.py +98 -0
  151. agentic_project_kit/gui_action_views.py +195 -0
  152. agentic_project_kit/gui_activity_log.py +76 -0
  153. agentic_project_kit/gui_button_catalog.py +605 -0
  154. agentic_project_kit/gui_cockpit.py +365 -0
  155. agentic_project_kit/gui_cockpit_actions.py +695 -0
  156. agentic_project_kit/gui_cockpit_common.py +172 -0
  157. agentic_project_kit/gui_cockpit_header.py +501 -0
  158. agentic_project_kit/gui_cockpit_sidebar.py +387 -0
  159. agentic_project_kit/gui_cockpit_task.py +748 -0
  160. agentic_project_kit/gui_command_projection.py +355 -0
  161. agentic_project_kit/gui_communication_modes.py +130 -0
  162. agentic_project_kit/gui_dry_run.py +219 -0
  163. agentic_project_kit/gui_gatekeeper_status.py +259 -0
  164. agentic_project_kit/gui_layout_plan.py +148 -0
  165. agentic_project_kit/gui_open_folder.py +18 -0
  166. agentic_project_kit/gui_output_status_panel.py +61 -0
  167. agentic_project_kit/gui_panel_state.py +47 -0
  168. agentic_project_kit/gui_presenter.py +62 -0
  169. agentic_project_kit/gui_readiness_gate.py +206 -0
  170. agentic_project_kit/gui_release_flow.py +59 -0
  171. agentic_project_kit/gui_task_editor.py +1077 -0
  172. agentic_project_kit/gui_tk_widgets.py +177 -0
  173. agentic_project_kit/gui_tkinter_renderer.py +87 -0
  174. agentic_project_kit/gui_tkinter_shell.py +1176 -0
  175. agentic_project_kit/gui_transfer_contract.py +28 -0
  176. agentic_project_kit/gui_viewmodel.py +698 -0
  177. agentic_project_kit/gui_window_guard.py +46 -0
  178. agentic_project_kit/handoff_freshness.py +390 -0
  179. agentic_project_kit/handoff_prompt.py +180 -0
  180. agentic_project_kit/handoff_state.py +254 -0
  181. agentic_project_kit/instruction_lint.py +455 -0
  182. agentic_project_kit/llm_context_carriers.py +66 -0
  183. agentic_project_kit/llm_execution_context.py +285 -0
  184. agentic_project_kit/local_command_stack.py +133 -0
  185. agentic_project_kit/local_feature_gate.py +82 -0
  186. agentic_project_kit/local_garbage_collector.py +269 -0
  187. agentic_project_kit/models.py +18 -0
  188. agentic_project_kit/mutation_lock_audit.py +421 -0
  189. agentic_project_kit/next_turn_evidence.py +253 -0
  190. agentic_project_kit/next_turn_merge_if_green.py +459 -0
  191. agentic_project_kit/next_turn_pr_status.py +255 -0
  192. agentic_project_kit/next_turn_result.py +183 -0
  193. agentic_project_kit/next_turn_runner.py +159 -0
  194. agentic_project_kit/next_turn_slot.py +107 -0
  195. agentic_project_kit/next_turn_status.py +274 -0
  196. agentic_project_kit/ns_legacy_reference_audit.py +258 -0
  197. agentic_project_kit/ns_slice_runner.py +16 -0
  198. agentic_project_kit/operational_handoff_projection.py +124 -0
  199. agentic_project_kit/output_contract.py +80 -0
  200. agentic_project_kit/output_repair.py +61 -0
  201. agentic_project_kit/pass_already_done.py +216 -0
  202. agentic_project_kit/patch_artifact_preflight.py +147 -0
  203. agentic_project_kit/patch_cycle_workflow.py +440 -0
  204. agentic_project_kit/patch_failure_discipline_audit.py +223 -0
  205. agentic_project_kit/patch_preflight.py +215 -0
  206. agentic_project_kit/path_literal_audit.py +612 -0
  207. agentic_project_kit/patterns.py +99 -0
  208. agentic_project_kit/planning_docs_consolidation_audit.py +313 -0
  209. agentic_project_kit/portability_shell_gate.py +40 -0
  210. agentic_project_kit/post_merge_handoff_refresh.py +56 -0
  211. agentic_project_kit/post_release.py +252 -0
  212. agentic_project_kit/post_release_closeout.py +616 -0
  213. agentic_project_kit/pr_cleanup.py +172 -0
  214. agentic_project_kit/pr_closeout.py +50 -0
  215. agentic_project_kit/pr_closeout_complete.py +344 -0
  216. agentic_project_kit/pr_create_or_skip.py +71 -0
  217. agentic_project_kit/pr_hygiene.py +210 -0
  218. agentic_project_kit/program_redundancy_audit.py +251 -0
  219. agentic_project_kit/project_direction.py +840 -0
  220. agentic_project_kit/protected_change_planner.py +194 -0
  221. agentic_project_kit/release.py +401 -0
  222. agentic_project_kit/release_doi_safety.py +24 -0
  223. agentic_project_kit/release_gate_core.py +174 -0
  224. agentic_project_kit/release_metadata_authority_gate.py +328 -0
  225. agentic_project_kit/release_metadata_prep.py +43 -0
  226. agentic_project_kit/release_notes.py +823 -0
  227. agentic_project_kit/release_prep_core.py +204 -0
  228. agentic_project_kit/release_prepare.py +369 -0
  229. agentic_project_kit/release_process_guardrails.py +247 -0
  230. agentic_project_kit/release_publish_core.py +114 -0
  231. agentic_project_kit/release_publish_orchestration.py +469 -0
  232. agentic_project_kit/release_state.py +552 -0
  233. agentic_project_kit/release_verify_core.py +175 -0
  234. agentic_project_kit/remote_branch_hygiene.py +223 -0
  235. agentic_project_kit/remote_next.py +89 -0
  236. agentic_project_kit/remote_next_closeout.py +101 -0
  237. agentic_project_kit/remote_next_order_contract.py +391 -0
  238. agentic_project_kit/removed_ns_commands.py +458 -0
  239. agentic_project_kit/removed_source_audit.py +225 -0
  240. agentic_project_kit/rendering.py +13 -0
  241. agentic_project_kit/repair_report.py +79 -0
  242. agentic_project_kit/repo_identity.py +59 -0
  243. agentic_project_kit/result_report_classifier.py +57 -0
  244. agentic_project_kit/rule_ack.py +105 -0
  245. agentic_project_kit/rule_preservation.py +58 -0
  246. agentic_project_kit/rule_refresh.py +174 -0
  247. agentic_project_kit/rule_registry_registration.py +322 -0
  248. agentic_project_kit/rule_registry_report.py +206 -0
  249. agentic_project_kit/rule_registry_validator.py +704 -0
  250. agentic_project_kit/rule_snapshot.py +98 -0
  251. agentic_project_kit/rule_source_validator.py +133 -0
  252. agentic_project_kit/run_summary.py +68 -0
  253. agentic_project_kit/run_summary_renderer.py +238 -0
  254. agentic_project_kit/runtime_validator.py +68 -0
  255. agentic_project_kit/safe_file_replace.py +35 -0
  256. agentic_project_kit/safe_patch.py +47 -0
  257. agentic_project_kit/safe_push.py +218 -0
  258. agentic_project_kit/safe_remove_diagnostic.py +51 -0
  259. agentic_project_kit/scaffold.py +69 -0
  260. agentic_project_kit/site_claims.py +444 -0
  261. agentic_project_kit/site_generator.py +1140 -0
  262. agentic_project_kit/slice_gate.py +225 -0
  263. agentic_project_kit/slice_runner.py +75 -0
  264. agentic_project_kit/standard_gates_audit_suite.py +167 -0
  265. agentic_project_kit/state_freshness.py +135 -0
  266. agentic_project_kit/status_current_state_audit.py +762 -0
  267. agentic_project_kit/successor_handoff_package.py +1763 -0
  268. agentic_project_kit/templates.py +292 -0
  269. agentic_project_kit/terminal_block_guard.py +166 -0
  270. agentic_project_kit/terminal_logging.py +275 -0
  271. agentic_project_kit/todo.py +97 -0
  272. agentic_project_kit/transfer_closeout.py +136 -0
  273. agentic_project_kit/transfer_continue.py +215 -0
  274. agentic_project_kit/transfer_local_runner.py +101 -0
  275. agentic_project_kit/transfer_operation_monitor.py +236 -0
  276. agentic_project_kit/transfer_post_merge_lifecycle.py +356 -0
  277. agentic_project_kit/transfer_post_merge_settle.py +383 -0
  278. agentic_project_kit/transfer_pr_actions.py +164 -0
  279. agentic_project_kit/transfer_remote_next.py +959 -0
  280. agentic_project_kit/transfer_repo_actions.py +2253 -0
  281. agentic_project_kit/transfer_runner.py +347 -0
  282. agentic_project_kit/transfer_safety_context.py +291 -0
  283. agentic_project_kit/transfer_state.py +483 -0
  284. agentic_project_kit/transfer_uplink.py +471 -0
  285. agentic_project_kit/transfer_workflow_next.py +148 -0
  286. agentic_project_kit/typed_work_order_evidence.py +58 -0
  287. agentic_project_kit/typed_work_order_queue.py +166 -0
  288. agentic_project_kit/typed_work_order_runner.py +168 -0
  289. agentic_project_kit/work_cycle.py +317 -0
  290. agentic_project_kit/work_discard_changes.py +129 -0
  291. agentic_project_kit/work_order_patch.py +199 -0
  292. agentic_project_kit/work_order_runner.py +120 -0
  293. agentic_project_kit/work_order_uploader.py +220 -0
  294. agentic_project_kit/work_order_validator.py +185 -0
  295. agentic_project_kit/work_orders.py +246 -0
  296. agentic_project_kit/workflow_guard.py +262 -0
  297. agentic_project_kit/workflow_summary_runner.py +124 -0
  298. agentic_project_kit/workspace.py +665 -0
  299. agentic_project_kit/workspace_adopt.py +471 -0
  300. agentic_project_kit/workspace_dpa_intake.py +406 -0
  301. agentic_project_kit/workspace_init.py +487 -0
  302. agentic_project_kit/workspace_lock.py +129 -0
  303. agentic_project_kit/workspace_remove.py +381 -0
  304. agentic_project_kit/workspace_upgrade.py +324 -0
  305. agentic_project_kit/wrapper_live_status.py +104 -0
  306. agentic_project_kit-1.0.1.dist-info/METADATA +724 -0
  307. agentic_project_kit-1.0.1.dist-info/RECORD +310 -0
  308. agentic_project_kit-1.0.1.dist-info/WHEEL +4 -0
  309. agentic_project_kit-1.0.1.dist-info/entry_points.txt +3 -0
  310. agentic_project_kit-1.0.1.dist-info/licenses/LICENSE +10 -0
@@ -0,0 +1,355 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, dataclass
4
+ from pathlib import Path
5
+ from typing import Any, Iterable, Sequence
6
+
7
+ from agentic_project_kit.command_manifest import SURFACE_VALUES, load_manifest
8
+
9
+ GUI_SURFACE_LAYERS: dict[str, str] = {
10
+ "orchestrator": "primary",
11
+ "diagnostic": "diagnostics",
12
+ "primitive": "expert",
13
+ }
14
+ GUI_DIAGNOSTIC_PRIORITY_VALUES = {
15
+ "not_diagnostic",
16
+ "common_blocker",
17
+ "claim_evidence",
18
+ "specialized_audit",
19
+ "reference_lookup",
20
+ "advanced_diagnostic",
21
+ }
22
+ GUI_CLAIM_EVIDENCE_VALUES = {
23
+ "none",
24
+ "gate_output_required",
25
+ "exact_ref_required",
26
+ "release_evidence_required",
27
+ "pr_or_remote_evidence_required",
28
+ }
29
+ GUI_SAFETY_REVIEW_VALUES = {
30
+ "read_only",
31
+ "dry_run_available",
32
+ "manual_safety_review",
33
+ "destructive_gate_required",
34
+ }
35
+
36
+ COMMON_BLOCKER_DIAGNOSTICS = {
37
+ "agentic-kit audit-status-current-state",
38
+ "agentic-kit check-docs",
39
+ "agentic-kit docs-audit",
40
+ "agentic-kit doctor",
41
+ "agentic-kit workflow status",
42
+ "agentic-kit workflow-guard check",
43
+ }
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class GuiCommandProjectionEntry:
48
+ qualified_name: str
49
+ group: str
50
+ surface: str
51
+ layer: str
52
+ safety: str
53
+ dry_run_available: bool
54
+ diagnostic_priority: str
55
+ claim_evidence: str
56
+ safety_review: str
57
+ when_to_use: str
58
+
59
+ def as_dict(self) -> dict[str, object]:
60
+ return asdict(self)
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class GuiCommandBinding:
65
+ source_id: str
66
+ wrapper_command: tuple[str, ...]
67
+ qualified_name: str
68
+ surface: str
69
+ layer: str
70
+ safety: str
71
+ diagnostic_priority: str
72
+ claim_evidence: str
73
+ safety_review: str
74
+
75
+ def as_dict(self) -> dict[str, object]:
76
+ data = asdict(self)
77
+ data["wrapper_command"] = list(self.wrapper_command)
78
+ return data
79
+
80
+
81
+ @dataclass(frozen=True)
82
+ class GuiCommandProjection:
83
+ root: str
84
+ command_count: int
85
+ entries: tuple[GuiCommandProjectionEntry, ...]
86
+ findings: tuple[str, ...]
87
+
88
+ @property
89
+ def ok(self) -> bool:
90
+ return not self.findings
91
+
92
+ @property
93
+ def default_layer(self) -> str:
94
+ return "primary"
95
+
96
+ @property
97
+ def default_entries(self) -> tuple[GuiCommandProjectionEntry, ...]:
98
+ return self.entries_for_layer(self.default_layer)
99
+
100
+ def entries_for_layer(self, layer: str) -> tuple[GuiCommandProjectionEntry, ...]:
101
+ return tuple(entry for entry in self.entries if entry.layer == layer)
102
+
103
+ def surface_counts(self) -> dict[str, int]:
104
+ return {
105
+ surface: len([entry for entry in self.entries if entry.surface == surface])
106
+ for surface in sorted(GUI_SURFACE_LAYERS)
107
+ }
108
+
109
+ def layer_counts(self) -> dict[str, int]:
110
+ return {
111
+ layer: len([entry for entry in self.entries if entry.layer == layer])
112
+ for layer in ("primary", "diagnostics", "expert")
113
+ }
114
+
115
+ def diagnostic_priority_counts(self) -> dict[str, int]:
116
+ return {
117
+ priority: len([entry for entry in self.entries if entry.diagnostic_priority == priority])
118
+ for priority in sorted(GUI_DIAGNOSTIC_PRIORITY_VALUES)
119
+ }
120
+
121
+ def guided_diagnostic_entries(self) -> tuple[GuiCommandProjectionEntry, ...]:
122
+ return tuple(
123
+ entry for entry in self.entries if entry.diagnostic_priority == "common_blocker"
124
+ )
125
+
126
+ def as_dict(self) -> dict[str, object]:
127
+ return {
128
+ "schema_version": 1,
129
+ "kind": "gui_command_projection",
130
+ "root": self.root,
131
+ "status": "PASS" if self.ok else "FAIL",
132
+ "default_layer": self.default_layer,
133
+ "command_count": self.command_count,
134
+ "surface_counts": self.surface_counts(),
135
+ "layer_counts": self.layer_counts(),
136
+ "diagnostic_priority_counts": self.diagnostic_priority_counts(),
137
+ "guided_diagnostic_count": len(self.guided_diagnostic_entries()),
138
+ "findings": list(self.findings),
139
+ "entries": [entry.as_dict() for entry in self.entries],
140
+ }
141
+
142
+
143
+ def build_gui_command_projection(
144
+ root: Path = Path("."),
145
+ *,
146
+ manifest: dict[str, Any] | None = None,
147
+ ) -> GuiCommandProjection:
148
+ data = manifest if manifest is not None else load_manifest(root)
149
+ commands = _manifest_commands(data)
150
+ entries: list[GuiCommandProjectionEntry] = []
151
+ findings: list[str] = []
152
+
153
+ for command in commands:
154
+ qualified = str(command.get("qualified_name") or "")
155
+ surface = str(command.get("surface") or "")
156
+ if surface not in SURFACE_VALUES:
157
+ findings.append(f"{qualified or '<unknown>'}: invalid surface {surface!r}")
158
+ continue
159
+ layer = GUI_SURFACE_LAYERS[surface]
160
+ entries.append(
161
+ GuiCommandProjectionEntry(
162
+ qualified_name=qualified,
163
+ group=str(command.get("group") or ""),
164
+ surface=surface,
165
+ layer=layer,
166
+ safety=str(command.get("safety") or ""),
167
+ dry_run_available=bool(command.get("dry_run_available")),
168
+ diagnostic_priority=diagnostic_priority_for_command(command),
169
+ claim_evidence=claim_evidence_for_command(command),
170
+ safety_review=safety_review_for_command(command),
171
+ when_to_use=str(command.get("when_to_use") or ""),
172
+ )
173
+ )
174
+
175
+ if not entries:
176
+ findings.append("command manifest contains no GUI-projectable commands")
177
+ for surface, layer in GUI_SURFACE_LAYERS.items():
178
+ if not any(entry.surface == surface for entry in entries):
179
+ findings.append(f"command manifest contains no {surface} commands for GUI layer {layer}")
180
+
181
+ return GuiCommandProjection(
182
+ root=root.resolve().as_posix(),
183
+ command_count=len(commands),
184
+ entries=tuple(sorted(entries, key=lambda entry: entry.qualified_name)),
185
+ findings=tuple(findings),
186
+ )
187
+
188
+
189
+ def gui_command_bindings(
190
+ sources: Iterable[object],
191
+ root: Path = Path("."),
192
+ *,
193
+ manifest: dict[str, Any] | None = None,
194
+ ) -> tuple[GuiCommandBinding, ...]:
195
+ data = manifest if manifest is not None else load_manifest(root)
196
+ bindings: list[GuiCommandBinding] = []
197
+ for source in sources:
198
+ wrapper = _wrapper_command(source)
199
+ if not _is_agentic_kit_wrapper(wrapper):
200
+ continue
201
+ command = resolve_manifest_command(wrapper, data)
202
+ if command is None:
203
+ continue
204
+ surface = str(command.get("surface") or "")
205
+ if surface not in SURFACE_VALUES:
206
+ continue
207
+ bindings.append(
208
+ GuiCommandBinding(
209
+ source_id=str(getattr(source, "command_id", getattr(source, "action_id", "<unknown>"))),
210
+ wrapper_command=wrapper,
211
+ qualified_name=str(command.get("qualified_name") or ""),
212
+ surface=surface,
213
+ layer=GUI_SURFACE_LAYERS[surface],
214
+ safety=str(command.get("safety") or ""),
215
+ diagnostic_priority=diagnostic_priority_for_command(command),
216
+ claim_evidence=claim_evidence_for_command(command),
217
+ safety_review=safety_review_for_command(command),
218
+ )
219
+ )
220
+ return tuple(bindings)
221
+
222
+
223
+ def diagnostic_priority_for_command(command: dict[str, Any]) -> str:
224
+ qualified = str(command.get("qualified_name") or "")
225
+ surface = str(command.get("surface") or "")
226
+ group = str(command.get("group") or "")
227
+ leaf = qualified.split()[-1] if qualified else ""
228
+ if surface != "diagnostic":
229
+ return "not_diagnostic"
230
+ if qualified in COMMON_BLOCKER_DIAGNOSTICS:
231
+ return "common_blocker"
232
+ claim_evidence = claim_evidence_for_command(command)
233
+ if claim_evidence in {
234
+ "exact_ref_required",
235
+ "release_evidence_required",
236
+ "pr_or_remote_evidence_required",
237
+ }:
238
+ return "claim_evidence"
239
+ if group in {"audit", "dpa"} or leaf.startswith("audit-") or leaf.endswith("-audit"):
240
+ return "specialized_audit"
241
+ if group in {"commands", "command", "reference"} or leaf in {"list", "show", "render-md"}:
242
+ return "reference_lookup"
243
+ return "advanced_diagnostic"
244
+
245
+
246
+ def claim_evidence_for_command(command: dict[str, Any]) -> str:
247
+ qualified = str(command.get("qualified_name") or "")
248
+ group = str(command.get("group") or "")
249
+ words = set(qualified.split())
250
+ if "dpa" in words and any(
251
+ marker in qualified
252
+ for marker in (
253
+ "adoption-assessment",
254
+ "closeout",
255
+ "probe",
256
+ "readiness",
257
+ "stable",
258
+ "strict",
259
+ )
260
+ ):
261
+ return "exact_ref_required"
262
+ if group == "release" or "release" in words or "post-release" in qualified:
263
+ return "release_evidence_required"
264
+ if group == "transfer" and any(
265
+ marker in qualified
266
+ for marker in (" pr-", " post-merge", " push-", " publish-", " merge")
267
+ ):
268
+ return "pr_or_remote_evidence_required"
269
+ if qualified in COMMON_BLOCKER_DIAGNOSTICS or qualified.startswith("agentic-kit audit-"):
270
+ return "gate_output_required"
271
+ return "none"
272
+
273
+
274
+ def safety_review_for_command(command: dict[str, Any]) -> str:
275
+ safety = str(command.get("safety") or "")
276
+ dry_run_available = bool(command.get("dry_run_available"))
277
+ if safety == "READ_ONLY":
278
+ return "read_only"
279
+ if safety == "DESTRUCTIVE":
280
+ return "destructive_gate_required"
281
+ if dry_run_available:
282
+ return "dry_run_available"
283
+ return "manual_safety_review"
284
+
285
+
286
+ def validate_gui_command_bindings(
287
+ sources: Iterable[object],
288
+ root: Path = Path("."),
289
+ *,
290
+ manifest: dict[str, Any] | None = None,
291
+ ) -> tuple[str, ...]:
292
+ data = manifest if manifest is not None else load_manifest(root)
293
+ findings: list[str] = []
294
+ for source in sources:
295
+ source_id = str(getattr(source, "command_id", getattr(source, "action_id", "<unknown>")))
296
+ wrapper = _wrapper_command(source)
297
+ if not wrapper:
298
+ continue
299
+ if not _is_agentic_kit_wrapper(wrapper):
300
+ continue
301
+ command = resolve_manifest_command(wrapper, data)
302
+ if command is None:
303
+ findings.append(
304
+ f"{source_id}: wrapper command is not registered in command manifest: "
305
+ + " ".join(wrapper)
306
+ )
307
+ continue
308
+ surface = str(command.get("surface") or "")
309
+ if surface not in SURFACE_VALUES:
310
+ findings.append(f"{source_id}: wrapper command has invalid surface {surface!r}")
311
+ return tuple(findings)
312
+
313
+
314
+ def resolve_manifest_command(
315
+ wrapper_command: Sequence[str],
316
+ manifest: dict[str, Any],
317
+ ) -> dict[str, Any] | None:
318
+ wrapper = tuple(str(part) for part in wrapper_command)
319
+ if not _is_agentic_kit_wrapper(wrapper):
320
+ return None
321
+ commands = _manifest_commands(manifest)
322
+ candidates: list[tuple[tuple[str, ...], dict[str, Any]]] = []
323
+ for command in commands:
324
+ qualified = str(command.get("qualified_name") or "")
325
+ if not qualified:
326
+ continue
327
+ vector = tuple(qualified.split())
328
+ candidates.append((vector, command))
329
+ for vector, command in sorted(candidates, key=lambda item: len(item[0]), reverse=True):
330
+ if tuple(wrapper[: len(vector)]) == vector:
331
+ return command
332
+ return None
333
+
334
+
335
+ def _manifest_commands(manifest: dict[str, Any]) -> list[dict[str, Any]]:
336
+ commands = manifest.get("commands") if isinstance(manifest, dict) else None
337
+ if not isinstance(commands, list):
338
+ return []
339
+ return [command for command in commands if isinstance(command, dict)]
340
+
341
+
342
+ def _wrapper_command(source: object) -> tuple[str, ...]:
343
+ if hasattr(source, "wrapper_command"):
344
+ wrapper = getattr(source, "wrapper_command")
345
+ elif hasattr(source, "command"):
346
+ wrapper = getattr(source, "command")
347
+ else:
348
+ wrapper = ()
349
+ if not isinstance(wrapper, tuple):
350
+ wrapper = tuple(wrapper or ())
351
+ return tuple(str(part) for part in wrapper)
352
+
353
+
354
+ def _is_agentic_kit_wrapper(wrapper: Sequence[str]) -> bool:
355
+ return bool(wrapper) and str(wrapper[0]) == "agentic-kit"
@@ -0,0 +1,130 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class CommunicationModeDefinition:
8
+ mode_id: str
9
+ label: str
10
+ role: str
11
+ is_default: bool
12
+ safety_note: str
13
+ explanation: str
14
+ example: str
15
+ next_step_hint: str
16
+ walkthrough_steps: tuple[str, ...]
17
+ default_expanded_groups: tuple[str, ...]
18
+
19
+
20
+ COMMUNICATION_MODE_DEFINITIONS: tuple[CommunicationModeDefinition, ...] = (
21
+ CommunicationModeDefinition(
22
+ mode_id="file_transfer",
23
+ label="File Transfer",
24
+ role="Standard",
25
+ is_default=True,
26
+ safety_note="Normal path: typed transfer files, work orders, reports, evidence, no chat paste required.",
27
+ explanation=(
28
+ "Normal mode. Write the task to the repo-backed transfer task, send g/go in chat, "
29
+ "then read the result. Minimizes copy-and-paste."
30
+ ),
31
+ example=(
32
+ "Example: Send the task, type g/go in the LLM chat, then use Read or "
33
+ "agentic-kit transfer continue --json for the repo-backed reply."
34
+ ),
35
+ next_step_hint="Type your task below, click Send, then write g in chat.",
36
+ walkthrough_steps=(
37
+ "Start new work.",
38
+ "Type your task and Send; it is published to the repo.",
39
+ "Write g in chat; the assistant reads the task and works.",
40
+ "Click Check, then Finish & publish.",
41
+ ),
42
+ default_expanded_groups=("task_editor",),
43
+ ),
44
+ CommunicationModeDefinition(
45
+ mode_id="remote",
46
+ label="Remote",
47
+ role="GitHub/PR/CI",
48
+ is_default=False,
49
+ safety_note="Remote work is visible here but mutations remain strictly wrapper- and gatekeeper-guarded.",
50
+ explanation=(
51
+ "PR/CI/GitHub focused mode. Use safe read-only checks and bounded wrappers; "
52
+ "do not bypass governance."
53
+ ),
54
+ example=(
55
+ "Example: Ask the assistant to work through PR/CI, then inspect "
56
+ "agentic-kit transfer patch-cycle-status --json and evidence links."
57
+ ),
58
+ next_step_hint="Open or check the pull request; CI verifies it before merge.",
59
+ walkthrough_steps=(
60
+ "Start new work.",
61
+ "Make your changes.",
62
+ "Check.",
63
+ "Finish & publish opens a pull request; CI verifies before merge.",
64
+ ),
65
+ default_expanded_groups=(),
66
+ ),
67
+ CommunicationModeDefinition(
68
+ mode_id="copy_paste",
69
+ label="Copy-and-Paste",
70
+ role="Recovery/Fallback",
71
+ is_default=False,
72
+ safety_note=(
73
+ "Fallback only for terminal loss, Python startup issues, filesystem errors, "
74
+ "network trouble before push, broken logs, or hard recovery."
75
+ ),
76
+ explanation=(
77
+ "Recovery fallback only for terminal loss or missing remote access. "
78
+ "Not the normal operating mode."
79
+ ),
80
+ example=(
81
+ "Example: Open a local terminal and paste exactly one complete recovery block "
82
+ "from the LLM, then return only LOG/RC or the requested evidence."
83
+ ),
84
+ next_step_hint="Run the shown command locally and paste the result back.",
85
+ walkthrough_steps=(
86
+ "Start new work.",
87
+ "Copy the assistant's command.",
88
+ "Run it locally.",
89
+ "Paste the result back. Use only when no repo connection works.",
90
+ ),
91
+ default_expanded_groups=("copy_paste_tools",),
92
+ ),
93
+ )
94
+
95
+ DEFAULT_COMMUNICATION_MODE = "file_transfer"
96
+
97
+
98
+ def communication_mode_definitions() -> tuple[CommunicationModeDefinition, ...]:
99
+ return COMMUNICATION_MODE_DEFINITIONS
100
+
101
+
102
+ def communication_mode_ids() -> frozenset[str]:
103
+ return frozenset(definition.mode_id for definition in COMMUNICATION_MODE_DEFINITIONS)
104
+
105
+
106
+ def communication_mode_definition(mode: str) -> CommunicationModeDefinition:
107
+ normalized = mode if mode in communication_mode_ids() else DEFAULT_COMMUNICATION_MODE
108
+ return next(
109
+ definition for definition in COMMUNICATION_MODE_DEFINITIONS if definition.mode_id == normalized
110
+ )
111
+
112
+
113
+ def communication_mode_explanation(mode: str) -> str:
114
+ return communication_mode_definition(mode).explanation
115
+
116
+
117
+ def communication_mode_example(mode: str) -> str:
118
+ return communication_mode_definition(mode).example
119
+
120
+
121
+ def communication_mode_next_step_hint(mode: str) -> str:
122
+ return communication_mode_definition(mode).next_step_hint
123
+
124
+
125
+ def communication_mode_walkthrough_steps(mode: str) -> tuple[str, ...]:
126
+ return communication_mode_definition(mode).walkthrough_steps
127
+
128
+
129
+ def communication_mode_default_expanded_groups(mode: str) -> tuple[str, ...]:
130
+ return communication_mode_definition(mode).default_expanded_groups
@@ -0,0 +1,219 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from types import SimpleNamespace
5
+ import importlib
6
+ from pathlib import Path
7
+
8
+ from agentic_project_kit.action_registry import list_actions
9
+ from agentic_project_kit.gui_presenter import build_no_window_presenter_result
10
+ from agentic_project_kit.gui_action_execution import run_bounded_read_only_action
11
+ from agentic_project_kit.gui_layout_plan import build_layout_plan, render_layout_plan
12
+ from agentic_project_kit.gui_tkinter_renderer import render_layout_to_tkinter, render_tkinter_result_summary
13
+ from agentic_project_kit.gui_window_guard import check_window_launch_ready, render_window_guard_result
14
+ from agentic_project_kit.gui_output_status_panel import build_output_status_panel, render_output_status_panel
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class GuiDryRunResult:
19
+ ok: bool
20
+ tkinter_available: bool
21
+ action_registry_available: bool
22
+ action_specs_available: bool
23
+ presenter_available: bool
24
+ presenter_action_count: int
25
+ presenter_preview: str
26
+ mode_guard_available: bool
27
+ shell_adapters_absent: bool
28
+ message: str
29
+
30
+ @property
31
+ def exit_code(self) -> int:
32
+ return 0 if self.ok else 1
33
+
34
+
35
+ class DryRunRoot:
36
+ def __init__(self) -> None:
37
+ self.title_value = ""
38
+ self.geometry_value = ""
39
+
40
+ def title(self, value: str) -> None:
41
+ self.title_value = value
42
+
43
+ def geometry(self, value: str) -> None:
44
+ self.geometry_value = value
45
+
46
+
47
+ def _module_available(name: str) -> bool:
48
+ try:
49
+ importlib.import_module(name)
50
+ except Exception:
51
+ return False
52
+ return True
53
+
54
+
55
+ def _mode_guard_available(project_root: Path) -> bool:
56
+ ns_path = project_root / "ns"
57
+ if not ns_path.exists():
58
+ return False
59
+ ns_text = ns_path.read_text(encoding="utf-8")
60
+ return "mode-check" in ns_text and "mode-write" in ns_text
61
+
62
+
63
+ def _shell_adapters_absent(project_root: Path) -> bool:
64
+ return not any((project_root / "tools").glob("ns_*.sh"))
65
+
66
+
67
+ def run_gui_dry_run(project_root: Path | None = None) -> GuiDryRunResult:
68
+ root = Path.cwd() if project_root is None else project_root
69
+ tkinter_available = _module_available("tkinter")
70
+ action_registry_available = _module_available("agentic_project_kit.action_registry")
71
+ action_specs_available = _module_available("agentic_project_kit.action_specs")
72
+ mode_guard_available = _mode_guard_available(root)
73
+ shell_adapters_absent = _shell_adapters_absent(root)
74
+ presenter = build_no_window_presenter_result(list_actions())
75
+ ok = all((
76
+ action_registry_available,
77
+ action_specs_available,
78
+ mode_guard_available,
79
+ shell_adapters_absent,
80
+ presenter.ok,
81
+ ))
82
+ message = "GUI dry-run passed without opening a window." if ok else "GUI dry-run failed."
83
+ return GuiDryRunResult(
84
+ ok=ok,
85
+ tkinter_available=tkinter_available,
86
+ action_registry_available=action_registry_available,
87
+ action_specs_available=action_specs_available,
88
+ presenter_available=presenter.ok,
89
+ presenter_action_count=presenter.action_count,
90
+ presenter_preview=presenter.rendered,
91
+ mode_guard_available=mode_guard_available,
92
+ shell_adapters_absent=shell_adapters_absent,
93
+ message=message,
94
+ )
95
+
96
+
97
+ def render_result(result: GuiDryRunResult) -> str:
98
+ lines = [
99
+ "GUI DRY RUN",
100
+ "Safety: no window is opened; no files, branches, tags, releases, PRs, or remote state are changed.",
101
+ f"tkinter_available={str(result.tkinter_available).lower()}",
102
+ f"window_launch_ready={str(check_window_launch_ready().ok).lower()}",
103
+ "tkinter_note=nonblocking for --dry-run; required only for real window launch",
104
+ f"action_registry_available={str(result.action_registry_available).lower()}",
105
+ f"action_specs_available={str(result.action_specs_available).lower()}",
106
+ f"presenter_available={str(result.presenter_available).lower()}",
107
+ f"presenter_action_count={result.presenter_action_count}",
108
+ "presenter_preview_begin",
109
+ result.presenter_preview,
110
+ "presenter_preview_end",
111
+ f"mode_guard_available={str(result.mode_guard_available).lower()}",
112
+ f"shell_adapters_absent={str(result.shell_adapters_absent).lower()}",
113
+ f"message={result.message}",
114
+ ]
115
+ plan = build_layout_plan()
116
+ rendered = render_layout_to_tkinter(DryRunRoot(), plan)
117
+ lines.extend([
118
+ "output_status_panel_begin",
119
+ render_output_status_panel(
120
+ build_output_status_panel(
121
+ branch="dry-run",
122
+ dirty=False,
123
+ latest_output="GUI dry-run initialized without executing an action.",
124
+ terminal_log="NONE",
125
+ terminal_log_remote="NONE",
126
+ terminal_log_local="NONE",
127
+ evidence_state="not_required",
128
+ summary="No command summary exists in dry-run mode.",
129
+ )
130
+ ),
131
+ "output_status_panel_end",
132
+ "layout_plan_begin",
133
+ render_layout_plan(plan),
134
+ "layout_plan_end",
135
+ "tkinter_render_begin",
136
+ render_tkinter_result_summary(rendered),
137
+ "tkinter_render_end",
138
+ "window_guard_begin",
139
+ render_window_guard_result(check_window_launch_ready()),
140
+ "window_guard_end",
141
+ "real_window_opened=false",
142
+ "### RESULT: PASS ###" if result.ok else "### RESULT: FAIL ###",
143
+ ])
144
+ return "\n".join(lines)
145
+
146
+ # GUI_EXECUTION_RESULT_WIRING_OVERRIDE_BEGIN
147
+ _run_gui_dry_run_base = run_gui_dry_run
148
+ _render_result_base = render_result
149
+
150
+ def run_gui_dry_run(project_root: Path | None = None, *, action_name: str | None = None):
151
+ base_result = _run_gui_dry_run_base(project_root)
152
+ if not action_name:
153
+ return base_result
154
+ execution_result = run_bounded_read_only_action(list_actions(), action_name)
155
+ return SimpleNamespace(base_result=base_result, execution_result=execution_result)
156
+
157
+ def render_result(result) -> str:
158
+ base_result = getattr(result, "base_result", result)
159
+ output = _render_result_base(base_result)
160
+ execution = getattr(result, "execution_result", None)
161
+ if execution is None:
162
+ return output
163
+ lines = output.splitlines()
164
+ safety_class = str(getattr(execution.safety_class, "value", execution.safety_class)).strip().lower().replace("_", "-").rsplit(".", 1)[-1]
165
+ execution_lines = [
166
+ "execution_result_begin",
167
+ "GUI ACTION EXECUTION RESULT",
168
+ f"action={execution.action_name}",
169
+ f"safety_class={safety_class}",
170
+ f"allowed={str(execution.allowed).lower()}",
171
+ f"executed={str(execution.executed).lower()}",
172
+ f"returncode={execution.returncode}",
173
+ f"message={execution.message}",
174
+ f"output={execution.output}",
175
+ "execution_result_end",
176
+ ]
177
+ if "real_window_opened=false" in lines:
178
+ index = lines.index("real_window_opened=false")
179
+ lines[index:index] = execution_lines
180
+ else:
181
+ lines.extend(execution_lines)
182
+ return chr(10).join(lines)
183
+
184
+ def _render_help() -> str:
185
+ return chr(10).join([
186
+ "usage: ./ns gui [--dry-run] [--action ACTION_NAME]",
187
+ "Safety: help only; no window is opened and no action is executed.",
188
+ "options:",
189
+ " --help, -h Show this help and exit successfully.",
190
+ " --dry-run Render the no-window GUI dry-run.",
191
+ " --action ACTION_NAME Include a bounded action execution result.",
192
+ "### RESULT: PASS ###",
193
+ ])
194
+
195
+
196
+ def main(argv: list[str] | None = None) -> int:
197
+ import sys
198
+ args = list(sys.argv[1:] if argv is None else argv)
199
+ action_name = None
200
+ if args in (["--help"], ["-h"]):
201
+ print(_render_help())
202
+ return 0
203
+ if args == ["--dry-run"]:
204
+ args = []
205
+ if args:
206
+ if len(args) == 2 and args[0] == "--action":
207
+ action_name = args[1]
208
+ else:
209
+ print("usage: ./ns gui [--dry-run] [--action ACTION_NAME]")
210
+ print("### RESULT: FAIL ###")
211
+ return 2
212
+ print(render_result(run_gui_dry_run(action_name=action_name)))
213
+ return 0
214
+ # GUI_EXECUTION_RESULT_WIRING_OVERRIDE_END
215
+
216
+ if __name__ == "__main__":
217
+ import sys
218
+
219
+ raise SystemExit(main(sys.argv[1:]))