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,285 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ import subprocess
6
+ from datetime import datetime, timezone
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from agentic_project_kit.workspace import LEGACY_DEFAULTS, load_workspace
11
+
12
+ try:
13
+ import yaml
14
+ except Exception: # pragma: no cover - PyYAML is expected in the project env.
15
+ yaml = None # type: ignore[assignment]
16
+
17
+
18
+ COMMAND_REFERENCE_JSON = Path(LEGACY_DEFAULTS.reference_root) / "agentic-kit-commands.json"
19
+ COMMAND_REFERENCE_MD = Path(LEGACY_DEFAULTS.reference_root) / "AGENTIC_KIT_COMMANDS.md"
20
+ COMPILED_CONTEXT = Path(".agentic/compiled_agent_context.yaml")
21
+ TRANSFER_SAFETY_RULES = Path(".agentic/transfer_safety_rules.yaml")
22
+ ONE_COMMAND_PROTOCOL = Path(".agentic/transfer/one_command_transfer_protocol.yaml")
23
+
24
+ CANONICAL_SOURCES = (
25
+ COMPILED_CONTEXT,
26
+ TRANSFER_SAFETY_RULES,
27
+ ONE_COMMAND_PROTOCOL,
28
+ COMMAND_REFERENCE_JSON,
29
+ COMMAND_REFERENCE_MD,
30
+ )
31
+
32
+ IMPORTANT_WRAPPERS = (
33
+ "agentic-kit transfer continue",
34
+ "agentic-kit transfer remote-next",
35
+ "agentic-kit transfer chat-switch-complete",
36
+ "agentic-kit transfer command-reference-refresh",
37
+ "agentic-kit transfer command-reference-check",
38
+ "agentic-kit transfer sync-main",
39
+ "agentic-kit transfer remote-work-start",
40
+ "agentic-kit transfer protected-diff-plan",
41
+ "agentic-kit transfer pr-create-complete",
42
+ "agentic-kit transfer pr-create-complete --post-merge-complete",
43
+ "agentic-kit transfer pr-complete",
44
+ "agentic-kit transfer post-merge-settle",
45
+ "agentic-kit transfer post-merge-complete",
46
+ "agentic-kit transfer post-merge-check",
47
+ "agentic-kit transfer repo-status",
48
+ "agentic-kit transfer run-and-log",
49
+ "agentic-kit transfer publish-last-report",
50
+ "agentic-kit transfer show-last-report",
51
+ )
52
+
53
+
54
+ def build_llm_execution_context(root: str | Path = ".") -> dict[str, Any]:
55
+ """Build a fresh machine-readable context capsule for LLM-facing reports.
56
+
57
+ The capsule is a generated projection. It is not a new source of truth.
58
+ It points back to canonical rule files, current command reference files, and
59
+ current git state so a successor chat can avoid reconstructing commands or
60
+ workflow policy from memory.
61
+ """
62
+
63
+ root_path = Path(root)
64
+ command_reference = _load_command_reference(root_path)
65
+ transfer_rules = _load_yaml(root_path / TRANSFER_SAFETY_RULES)
66
+ protocol = _load_yaml(root_path / ONE_COMMAND_PROTOCOL)
67
+
68
+ return {
69
+ "schema_version": 1,
70
+ "kind": "llm_execution_context",
71
+ "generated_at_utc": datetime.now(timezone.utc).isoformat(),
72
+ "generated_from_current_repo": True,
73
+ "projection_only_not_source_of_truth": True,
74
+ "source_files": [str(path) for path in CANONICAL_SOURCES],
75
+ "source_hashes": _source_hashes(root_path),
76
+ "command_reference": {
77
+ "json": str(COMMAND_REFERENCE_JSON),
78
+ "markdown": str(COMMAND_REFERENCE_MD),
79
+ "command_count": _command_count(command_reference),
80
+ "generated_by": _command_reference_meta(command_reference).get("generated_by", ""),
81
+ "must_not_reconstruct_commands_from_memory": True,
82
+ "important_wrappers": _important_wrappers(command_reference),
83
+ },
84
+ "execution_policy": _execution_policy(transfer_rules, protocol),
85
+ "llm_to_local_transfer_policy": transfer_rules.get("llm_to_local_transfer_policy", {}),
86
+ "command_integration_governance": transfer_rules.get("command_integration_governance", {}),
87
+ "context_quality": {
88
+ "generated_from_current_repo_sources": True,
89
+ "source_hashes_present": True,
90
+ "source_hashes_complete": all(_source_hashes(root_path).values()),
91
+ "machine_readable": True,
92
+ "projection_only_not_source_of_truth": True,
93
+ "must_be_regenerated_from_rules_not_cached": True,
94
+ },
95
+ "running_chat_refresh_contract": transfer_rules.get("running_chat_refresh_contract", {}),
96
+ "shell_placeholder_policy": transfer_rules.get("shell_placeholder_policy", {}),
97
+ "canonical_lifecycle": {
98
+ "new_pr_single_command_text": "agentic-kit transfer pr-create-complete --post-merge-complete",
99
+ "verify_context_refresh_command": "agentic-kit transfer verify-llm-context-refresh",
100
+ "feature_branch_pre_pr_gate": "agentic-kit transfer repo-status",
101
+ "forbidden_feature_branch_pre_pr_gate": "agentic-kit transfer post-merge-check",
102
+ "fresh_llm_context_before_pr": [
103
+ "./.venv/bin/agentic-kit transfer prepare-successor-handoff --render-prompt",
104
+ "./.venv/bin/agentic-kit transfer publish-last-report",
105
+ "./.venv/bin/agentic-kit transfer require-fresh-llm-context --json",
106
+ ],
107
+ "post_merge_checks_belong_to_main": True,
108
+ "post_merge_check_after_merge_only": True,
109
+ "do_not_use_stale_prompt_text_as_handoff_source": True,
110
+ "new_pr_single_command": [
111
+ "./.venv/bin/agentic-kit",
112
+ "transfer",
113
+ "pr-create-complete",
114
+ "--post-merge-complete",
115
+ "--base",
116
+ "main",
117
+ "--head",
118
+ "current",
119
+ "--merge-method",
120
+ "squash",
121
+ ],
122
+ "existing_pr_requires_concrete_number": [
123
+ "./.venv/bin/agentic-kit",
124
+ "transfer",
125
+ "pr-complete",
126
+ "use-the-concrete-pr-number-from-wrapper-output",
127
+ "--expected-head-sha",
128
+ "current",
129
+ "--merge-method",
130
+ "squash",
131
+ ],
132
+ "post_merge_requires_concrete_number": [
133
+ "./.venv/bin/agentic-kit transfer sync-main",
134
+ "./.venv/bin/agentic-kit transfer post-merge-settle --after-pr use-the-concrete-pr-number-from-wrapper-output",
135
+ "./.venv/bin/agentic-kit transfer repo-status",
136
+ ],
137
+ "shell_placeholder_policy": {
138
+ "no_angle_bracket_placeholders_in_executable_blocks": True,
139
+ "if_pr_number_unknown_first_run_diagnostic_not_shell_placeholder": True,
140
+ "known_failure_example": "zsh parses angle-bracket placeholders as redirection",
141
+ },
142
+ },
143
+ "terminal_resilience": {
144
+ "avoid_process_ended_terminal_dead_end": True,
145
+ "do_not_require_terminal_restart_after_script_errors": True,
146
+ "patch_scripts_should_report_status_and_return_to_shell": True,
147
+ "avoid_unnecessary_raise_system_exit_in_diagnostics": True,
148
+ "avoid_set_euo_pipefail_in_exploratory_patch_blocks": True,
149
+ },
150
+ "patch_generation_policy": {
151
+ "avoid_heredoc_quote_escape_drift": True,
152
+ "avoid_long_shell_mutation_blocks": True,
153
+ "avoid_fragile_multiline_inline_python_anchors": True,
154
+ "avoid_raw_newlines_inside_generated_python_string_literals": True,
155
+ "prefer_small_python_patch_scripts_with_stable_checks": True,
156
+ },
157
+ "volatile_cleanup": {
158
+ "known_paths": [
159
+ ".agentic/transfer/inbox/next_command.py.txt",
160
+ ".agentic/transfer/outbox/last_result.txt",
161
+ "docs/reports/terminal/transfer_handoff_reports/latest-transfer-handoff-report.json",
162
+ "docs/reports/terminal/transfer_handoff_reports/latest-transfer-handoff-report.log",
163
+ ],
164
+ "only_when_not_target_changes": True,
165
+ "must_not_discard_substantive_changes": True,
166
+ },
167
+ "git_state": _git_state(root_path),
168
+ }
169
+
170
+
171
+ def attach_llm_execution_context(payload: dict[str, Any], root: str | Path = ".") -> dict[str, Any]:
172
+ payload["llm_execution_context"] = build_llm_execution_context(root)
173
+ return payload
174
+
175
+
176
+ def _source_hashes(root: Path) -> dict[str, str]:
177
+ hashes: dict[str, str] = {}
178
+ for relative in CANONICAL_SOURCES:
179
+ path = root / relative
180
+ if not path.exists():
181
+ hashes[str(relative)] = "missing"
182
+ continue
183
+ hashes[str(relative)] = hashlib.sha256(path.read_bytes()).hexdigest()
184
+ return hashes
185
+
186
+
187
+ def _load_yaml(path: Path) -> dict[str, Any]:
188
+ if yaml is None or not path.exists():
189
+ return {}
190
+ raw = yaml.safe_load(path.read_text(encoding="utf-8"))
191
+ return raw if isinstance(raw, dict) else {}
192
+
193
+
194
+ def _load_command_reference(root: Path) -> dict[str, Any]:
195
+ path = load_workspace(root).reference_file("agentic-kit-commands.json")
196
+ if not path.exists():
197
+ return {}
198
+ try:
199
+ raw = json.loads(path.read_text(encoding="utf-8"))
200
+ except json.JSONDecodeError:
201
+ return {}
202
+ return raw if isinstance(raw, dict) else {}
203
+
204
+
205
+ def _command_reference_meta(command_reference: dict[str, Any]) -> dict[str, Any]:
206
+ meta = command_reference.get("metadata")
207
+ if isinstance(meta, dict):
208
+ return meta
209
+ return {key: command_reference.get(key, "") for key in ("generated_by", "kind")}
210
+
211
+
212
+ def _iter_commands(command_reference: dict[str, Any]) -> list[dict[str, Any]]:
213
+ for key in ("commands", "items"):
214
+ value = command_reference.get(key)
215
+ if isinstance(value, list):
216
+ return [item for item in value if isinstance(item, dict)]
217
+ return []
218
+
219
+
220
+ def _command_count(command_reference: dict[str, Any]) -> int:
221
+ return len(_iter_commands(command_reference))
222
+
223
+
224
+ def _important_wrappers(command_reference: dict[str, Any]) -> list[dict[str, Any]]:
225
+ commands = _iter_commands(command_reference)
226
+ by_name = {
227
+ str(item.get("qualified_name", "")): item
228
+ for item in commands
229
+ if isinstance(item.get("qualified_name", ""), str)
230
+ }
231
+ wrappers: list[dict[str, Any]] = []
232
+ for qualified in IMPORTANT_WRAPPERS:
233
+ item = by_name.get(qualified)
234
+ wrappers.append(
235
+ {
236
+ "qualified_name": qualified,
237
+ "present": item is not None,
238
+ "help": str(item.get("help", "")) if item else "",
239
+ "options": _option_names(item or {}),
240
+ }
241
+ )
242
+ return wrappers
243
+
244
+
245
+ def _option_names(command: dict[str, Any]) -> list[str]:
246
+ options: list[str] = []
247
+ for param in command.get("params", []) or command.get("parameters", []) or []:
248
+ if not isinstance(param, dict):
249
+ continue
250
+ for field in ("opts", "option_strings", "names"):
251
+ raw = param.get(field)
252
+ if isinstance(raw, list):
253
+ options.extend(str(item) for item in raw if str(item).startswith("-"))
254
+ raw_name = param.get("opts")
255
+ if isinstance(raw_name, str) and raw_name.startswith("-"):
256
+ options.append(raw_name)
257
+ return sorted(dict.fromkeys(options))
258
+
259
+
260
+ def _execution_policy(transfer_rules: dict[str, Any], protocol: dict[str, Any]) -> dict[str, Any]:
261
+ encoded = json.dumps([transfer_rules, protocol], sort_keys=True, ensure_ascii=False)
262
+ return {
263
+ "wrapper_first": "wrapper-first" in encoded or "wrapper_first" in encoded,
264
+ "transfer_file_second": "transfer-file second" in encoded or "transfer_file_second" in encoded,
265
+ "copy_paste_last": "copy/paste shell only as fallback" in encoded or "copy_paste" in encoded,
266
+ "post_merge_settle_required_after_merge": "post-merge-settle --after-pr" in encoded,
267
+ "post_merge_complete_required_after_merge": "post-merge-complete --after-pr" in encoded,
268
+ "run_and_log_is_diagnostic_only": "run-and-log only for diagnostics" in encoded or "not as replacement for post-merge-complete" in encoded,
269
+ "read_remote_report_before_planning_after_g": "continue_from_chat_memory" in encoded and "read_latest_remote_transfer_report_first" in encoded,
270
+ }
271
+
272
+
273
+ def _git_state(root: Path) -> dict[str, Any]:
274
+ return {
275
+ "branch": _git(root, ["git", "branch", "--show-current"]),
276
+ "head": _git(root, ["git", "rev-parse", "HEAD"]),
277
+ "status_short": _git(root, ["git", "status", "--short"]),
278
+ }
279
+
280
+
281
+ def _git(root: Path, cmd: list[str]) -> str:
282
+ completed = subprocess.run(cmd, cwd=root, text=True, capture_output=True)
283
+ if completed.returncode != 0:
284
+ return ""
285
+ return completed.stdout.strip()
@@ -0,0 +1,133 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import uuid
5
+ from datetime import UTC
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from agentic_project_kit.workspace import LEGACY_DEFAULTS, load_workspace
11
+
12
+ LOCAL_COMMAND_STACK_STATE_FILE = "local-command-stack-state.json"
13
+ LOCAL_COMMAND_STACK_STATE = Path(LEGACY_DEFAULTS.tmp_root) / LOCAL_COMMAND_STACK_STATE_FILE
14
+ DEFAULT_MAX_AGE_SECONDS = 6 * 60 * 60
15
+
16
+
17
+ def _utc_now() -> datetime:
18
+ return datetime.now(UTC)
19
+
20
+
21
+ def _iso(dt: datetime) -> str:
22
+ return dt.astimezone(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
23
+
24
+
25
+ def _parse_iso(value: object) -> datetime | None:
26
+ if not isinstance(value, str) or not value.strip():
27
+ return None
28
+ try:
29
+ return datetime.fromisoformat(value.replace("Z", "+00:00")).astimezone(UTC)
30
+ except ValueError:
31
+ return None
32
+
33
+
34
+ def _state_path(project_root: Path) -> Path:
35
+ return load_workspace(project_root).local_command_stack_state_path()
36
+
37
+
38
+ def read_local_command_stack(project_root: Path = Path(".")) -> dict[str, Any]:
39
+ path = _state_path(project_root)
40
+ if not path.exists():
41
+ return {
42
+ "schema_version": 1,
43
+ "kind": "local_command_stack_state",
44
+ "active": False,
45
+ "command_stack_id": "",
46
+ "started_at_utc": "",
47
+ "updated_at_utc": "",
48
+ "reason": "missing_state_file",
49
+ }
50
+ try:
51
+ data = json.loads(path.read_text(encoding="utf-8"))
52
+ except json.JSONDecodeError:
53
+ return {
54
+ "schema_version": 1,
55
+ "kind": "local_command_stack_state",
56
+ "active": False,
57
+ "command_stack_id": "",
58
+ "started_at_utc": "",
59
+ "updated_at_utc": "",
60
+ "reason": "invalid_state_file",
61
+ }
62
+ if not isinstance(data, dict):
63
+ return {
64
+ "schema_version": 1,
65
+ "kind": "local_command_stack_state",
66
+ "active": False,
67
+ "command_stack_id": "",
68
+ "started_at_utc": "",
69
+ "updated_at_utc": "",
70
+ "reason": "invalid_state_shape",
71
+ }
72
+ data.setdefault("schema_version", 1)
73
+ data.setdefault("kind", "local_command_stack_state")
74
+ data.setdefault("active", False)
75
+ data.setdefault("command_stack_id", "")
76
+ data.setdefault("started_at_utc", "")
77
+ data.setdefault("updated_at_utc", "")
78
+ return data
79
+
80
+
81
+ def write_local_command_stack(project_root: Path, state: dict[str, Any]) -> dict[str, Any]:
82
+ path = _state_path(project_root)
83
+ path.parent.mkdir(parents=True, exist_ok=True)
84
+ path.write_text(json.dumps(state, indent=2, sort_keys=True) + "\n", encoding="utf-8")
85
+ return state
86
+
87
+
88
+ def begin_local_command_stack(project_root: Path = Path("."), *, reason: str = "explicit_begin") -> dict[str, Any]:
89
+ now = _iso(_utc_now())
90
+ state = {
91
+ "schema_version": 1,
92
+ "kind": "local_command_stack_state",
93
+ "active": True,
94
+ "command_stack_id": f"local-{uuid.uuid4().hex}",
95
+ "started_at_utc": now,
96
+ "updated_at_utc": now,
97
+ "reason": reason,
98
+ }
99
+ return write_local_command_stack(project_root, state)
100
+
101
+
102
+ def end_local_command_stack(project_root: Path = Path("."), *, reason: str = "explicit_end") -> dict[str, Any]:
103
+ state = read_local_command_stack(project_root)
104
+ state["active"] = False
105
+ state["updated_at_utc"] = _iso(_utc_now())
106
+ state["reason"] = reason
107
+ return write_local_command_stack(project_root, state)
108
+
109
+
110
+ def current_or_begin_local_command_stack(
111
+ project_root: Path = Path("."),
112
+ *,
113
+ max_age_seconds: int = DEFAULT_MAX_AGE_SECONDS,
114
+ ) -> dict[str, Any]:
115
+ state = read_local_command_stack(project_root)
116
+ stack_id = str(state.get("command_stack_id", "")).strip()
117
+ active = bool(state.get("active"))
118
+ started = _parse_iso(state.get("started_at_utc"))
119
+ now = _utc_now()
120
+
121
+ if active and stack_id and started is not None:
122
+ age_seconds = int((now - started).total_seconds())
123
+ if 0 <= age_seconds <= max_age_seconds:
124
+ state["updated_at_utc"] = _iso(now)
125
+ state["reason"] = "active_reused"
126
+ return write_local_command_stack(project_root, state)
127
+
128
+ return begin_local_command_stack(project_root, reason="implicit_begin")
129
+
130
+
131
+ def current_or_begin_local_command_stack_id(project_root: Path = Path(".")) -> str:
132
+ state = current_or_begin_local_command_stack(project_root)
133
+ return str(state["command_stack_id"])
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import subprocess
5
+ import sys
6
+ from collections.abc import Sequence
7
+
8
+ PYTHON_RUFF_TARGETS = ("src", "tests")
9
+
10
+
11
+ def _env_with_src() -> dict[str, str]:
12
+ env = os.environ.copy()
13
+ current = env.get("PYTHONPATH")
14
+ env["PYTHONPATH"] = "src" if not current else "src" + os.pathsep + current
15
+ return env
16
+
17
+
18
+ def _section(title: str) -> None:
19
+ print()
20
+ print(title)
21
+
22
+
23
+ def _run(command: Sequence[str], *, env: dict[str, str] | None = None) -> int:
24
+ completed = subprocess.run(list(command), env=env, check=False)
25
+ return int(completed.returncode)
26
+
27
+
28
+ def _ruff_command() -> list[str]:
29
+ return [sys.executable, "-m", "ruff", "check", *PYTHON_RUFF_TARGETS]
30
+
31
+
32
+ def run_local_feature_gate(args: Sequence[str] | None = None, *, include_pr_hygiene: bool = False) -> int:
33
+ pytest_args = list(args or [])
34
+ status = 0
35
+
36
+ _section("### NS DEV LOCAL FEATURE GATE ###")
37
+ print("Safety: local feature gate; no git pull, push, merge, tag, release, or branch cleanup.")
38
+
39
+ _section("### BRANCH / STATUS ###")
40
+ status = _run(["git", "branch", "--show-current"]) or status
41
+ status = _run(["git", "status", "--short"]) or status
42
+
43
+ env = _env_with_src()
44
+
45
+ _section("### PYTEST ###")
46
+ status = _run([sys.executable, "-m", "pytest", "-q", *pytest_args], env=env) or status
47
+
48
+ _section("### RUFF ###")
49
+ status = _run(_ruff_command(), env=env) or status
50
+
51
+ _section("### CHECK DOCS ###")
52
+ status = _run([sys.executable, "-m", "agentic_project_kit.cli", "check-docs"], env=env) or status
53
+
54
+ _section("### DOCTOR ###")
55
+ status = _run([sys.executable, "-m", "agentic_project_kit.cli", "doctor"], env=env) or status
56
+
57
+ if include_pr_hygiene:
58
+ _section("### PR HYGIENE ###")
59
+ status = _run([sys.executable, "-m", "agentic_project_kit.cli", "pr-hygiene"], env=env) or status
60
+
61
+ _section("### FINAL STATUS ###")
62
+ status = _run(["git", "branch", "--show-current"]) or status
63
+ status = _run(["git", "status", "--short"]) or status
64
+
65
+ if status == 0:
66
+ print("Local feature gate passed.")
67
+ return 0
68
+ print("Local feature gate failed.")
69
+ return 1
70
+
71
+
72
+ def main(argv: Sequence[str] | None = None) -> int:
73
+ raw_args = list(sys.argv[1:] if argv is None else argv)
74
+ include_pr_hygiene = False
75
+ if "--include-pr-hygiene" in raw_args:
76
+ include_pr_hygiene = True
77
+ raw_args.remove("--include-pr-hygiene")
78
+ return run_local_feature_gate(raw_args, include_pr_hygiene=include_pr_hygiene)
79
+
80
+
81
+ if __name__ == "__main__":
82
+ raise SystemExit(main())