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,591 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import asdict, dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from typer.main import get_command
|
|
10
|
+
|
|
11
|
+
from agentic_project_kit.workspace import LEGACY_DEFAULTS, load_workspace
|
|
12
|
+
|
|
13
|
+
JSON_PATH = Path(LEGACY_DEFAULTS.reference_root) / "agentic-kit-commands.json"
|
|
14
|
+
MD_PATH = Path(LEGACY_DEFAULTS.reference_root) / "AGENTIC_KIT_COMMANDS.md"
|
|
15
|
+
SAFETY_VALUES = {"READ_ONLY", "BOUNDED", "DESTRUCTIVE"}
|
|
16
|
+
SURFACE_VALUES = {"orchestrator", "diagnostic", "primitive"}
|
|
17
|
+
|
|
18
|
+
RAW_REPLACEMENTS: dict[str, tuple[str, ...]] = {
|
|
19
|
+
"agentic-kit transfer push-current": ("git push",),
|
|
20
|
+
"agentic-kit transfer commit": ("git commit",),
|
|
21
|
+
"agentic-kit transfer branch-create": ("git switch -c", "git checkout -b"),
|
|
22
|
+
"agentic-kit transfer pr-create-complete": ("gh pr create",),
|
|
23
|
+
"agentic-kit transfer pr-merge-safe": ("gh pr merge",),
|
|
24
|
+
"agentic-kit transfer delete-merged-work-branch": ("git push --delete", "git branch -D"),
|
|
25
|
+
"agentic-kit release ready": ("git tag", "gh release create"),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
LIFECYCLE_RANKS: dict[str, int] = {
|
|
29
|
+
"agentic-kit init": 0,
|
|
30
|
+
"agentic-kit workspace adopt": 10,
|
|
31
|
+
"agentic-kit workspace init": 20,
|
|
32
|
+
"agentic-kit workspace dpa-intake": 21,
|
|
33
|
+
"agentic-kit workspace upgrade": 25,
|
|
34
|
+
"agentic-kit work start": 30,
|
|
35
|
+
"agentic-kit work recover": 35,
|
|
36
|
+
"agentic-kit workflow go": 40,
|
|
37
|
+
"agentic-kit transfer remote-work-start": 45,
|
|
38
|
+
"agentic-kit transfer remote-next": 46,
|
|
39
|
+
"agentic-kit work finish": 50,
|
|
40
|
+
"agentic-kit transfer pr-create-complete": 55,
|
|
41
|
+
"agentic-kit transfer pr-complete": 56,
|
|
42
|
+
"agentic-kit transfer pr-closeout-complete": 57,
|
|
43
|
+
"agentic-kit transfer post-merge-settle": 60,
|
|
44
|
+
"agentic-kit transfer post-merge-complete": 61,
|
|
45
|
+
"agentic-kit transfer chat-switch-complete": 62,
|
|
46
|
+
"agentic-kit transfer admin-refresh-pr": 63,
|
|
47
|
+
"agentic-kit release ready": 70,
|
|
48
|
+
"agentic-kit release prepare": 71,
|
|
49
|
+
"agentic-kit release-prep": 72,
|
|
50
|
+
"agentic-kit release-publish": 73,
|
|
51
|
+
"agentic-kit post-release-doi-closeout": 74,
|
|
52
|
+
"agentic-kit docs lifecycle sweep": 80,
|
|
53
|
+
"agentic-kit dpa final-closeout-check": 90,
|
|
54
|
+
"agentic-kit workspace remove": 95,
|
|
55
|
+
"agentic-kit artifact-gc": 100,
|
|
56
|
+
"agentic-kit github-create": 110,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class CommandManifestFinding:
|
|
62
|
+
severity: str
|
|
63
|
+
code: str
|
|
64
|
+
message: str
|
|
65
|
+
|
|
66
|
+
def as_dict(self) -> dict[str, str]:
|
|
67
|
+
return asdict(self)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass(frozen=True)
|
|
71
|
+
class CommandManifestAudit:
|
|
72
|
+
root: str
|
|
73
|
+
findings: tuple[CommandManifestFinding, ...]
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def blockers(self) -> tuple[CommandManifestFinding, ...]:
|
|
77
|
+
return tuple(finding for finding in self.findings if finding.severity == "BLOCK")
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def ok(self) -> bool:
|
|
81
|
+
return not self.blockers
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def status(self) -> str:
|
|
85
|
+
return "PASS" if self.ok else "BLOCK"
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def returncode(self) -> int:
|
|
89
|
+
return 0 if self.ok else 2
|
|
90
|
+
|
|
91
|
+
def as_dict(self) -> dict[str, object]:
|
|
92
|
+
return {
|
|
93
|
+
"schema_version": 1,
|
|
94
|
+
"kind": "command_manifest_audit",
|
|
95
|
+
"root": self.root,
|
|
96
|
+
"status": self.status,
|
|
97
|
+
"finding_count": len(self.findings),
|
|
98
|
+
"blocker_count": len(self.blockers),
|
|
99
|
+
"findings": [finding.as_dict() for finding in self.findings],
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def manifest_sha(commands: list[dict[str, Any]]) -> str:
|
|
104
|
+
canonical = json.dumps(commands, sort_keys=True, separators=(",", ":")).encode("utf-8")
|
|
105
|
+
return hashlib.sha256(canonical).hexdigest()[:12]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _jsonable(value: Any) -> Any:
|
|
109
|
+
if value is None or isinstance(value, (str, int, float, bool)):
|
|
110
|
+
return value
|
|
111
|
+
if isinstance(value, (list, tuple)):
|
|
112
|
+
return [_jsonable(item) for item in value]
|
|
113
|
+
if isinstance(value, dict):
|
|
114
|
+
return {str(k): _jsonable(v) for k, v in value.items()}
|
|
115
|
+
return repr(value)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _param_record(param: Any) -> dict[str, Any]:
|
|
119
|
+
record: dict[str, Any] = {
|
|
120
|
+
"name": param.name,
|
|
121
|
+
"param_type": param.__class__.__name__,
|
|
122
|
+
"required": bool(getattr(param, "required", False)),
|
|
123
|
+
"default": _jsonable(getattr(param, "default", None)),
|
|
124
|
+
"metavar": getattr(param, "metavar", None),
|
|
125
|
+
}
|
|
126
|
+
if hasattr(param, "opts"):
|
|
127
|
+
record["opts"] = list(getattr(param, "opts", []))
|
|
128
|
+
record["secondary_opts"] = list(getattr(param, "secondary_opts", []))
|
|
129
|
+
record["help"] = getattr(param, "help", None) or ""
|
|
130
|
+
record["is_flag"] = bool(getattr(param, "is_flag", False))
|
|
131
|
+
record["multiple"] = bool(getattr(param, "multiple", False))
|
|
132
|
+
return record
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _record_for(path: list[str], command: Any) -> dict[str, Any]:
|
|
136
|
+
group = "root" if len(path) == 1 else " ".join(path[:-1])
|
|
137
|
+
qualified_name = "agentic-kit " + " ".join(path)
|
|
138
|
+
params = [_param_record(param) for param in command.params]
|
|
139
|
+
command_record: dict[str, Any] = {
|
|
140
|
+
"group": group,
|
|
141
|
+
"name": path[-1],
|
|
142
|
+
"path": path,
|
|
143
|
+
"qualified_name": qualified_name,
|
|
144
|
+
"help": command.help or "",
|
|
145
|
+
"params": params,
|
|
146
|
+
}
|
|
147
|
+
return _with_manifest_fields(command_record)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _walk(path: list[str], command: Any) -> list[dict[str, Any]]:
|
|
151
|
+
commands = getattr(command, "commands", None)
|
|
152
|
+
if isinstance(commands, dict):
|
|
153
|
+
records: list[dict[str, Any]] = []
|
|
154
|
+
for name in sorted(commands):
|
|
155
|
+
records.extend(_walk([*path, name], commands[name]))
|
|
156
|
+
return records
|
|
157
|
+
return [_record_for(path, command)]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _leaf(command: dict[str, Any]) -> str:
|
|
161
|
+
path = command.get("path")
|
|
162
|
+
if isinstance(path, list) and path:
|
|
163
|
+
return str(path[-1])
|
|
164
|
+
return str(command.get("name") or command.get("qualified_name") or "").rsplit(" ", 1)[-1]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _all_opts(command: dict[str, Any]) -> set[str]:
|
|
168
|
+
opts: set[str] = set()
|
|
169
|
+
for param in command.get("params") or []:
|
|
170
|
+
if not isinstance(param, dict):
|
|
171
|
+
continue
|
|
172
|
+
opts.update(str(opt) for opt in param.get("opts") or [])
|
|
173
|
+
opts.update(str(opt) for opt in param.get("secondary_opts") or [])
|
|
174
|
+
return opts
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def infer_safety(command: dict[str, Any]) -> str:
|
|
178
|
+
qualified = str(command.get("qualified_name") or "")
|
|
179
|
+
group = str(command.get("group") or "")
|
|
180
|
+
leaf = _leaf(command)
|
|
181
|
+
|
|
182
|
+
exact_read_only_commands = {
|
|
183
|
+
"agentic-kit handoff post-merge-refresh-status",
|
|
184
|
+
"agentic-kit transfer post-merge-check",
|
|
185
|
+
"agentic-kit workspace adopt",
|
|
186
|
+
}
|
|
187
|
+
if qualified in exact_read_only_commands:
|
|
188
|
+
return "READ_ONLY"
|
|
189
|
+
|
|
190
|
+
destructive_terms = (
|
|
191
|
+
"delete",
|
|
192
|
+
"merge",
|
|
193
|
+
"publish",
|
|
194
|
+
"release-publish",
|
|
195
|
+
"branch-delete",
|
|
196
|
+
"delete-merged-work-branch",
|
|
197
|
+
)
|
|
198
|
+
if any(term in qualified for term in destructive_terms):
|
|
199
|
+
return "DESTRUCTIVE"
|
|
200
|
+
|
|
201
|
+
read_only_leafs = {
|
|
202
|
+
"check",
|
|
203
|
+
"check-docs",
|
|
204
|
+
"check-todo",
|
|
205
|
+
"doctor",
|
|
206
|
+
"status",
|
|
207
|
+
"state",
|
|
208
|
+
"list",
|
|
209
|
+
"show",
|
|
210
|
+
"report",
|
|
211
|
+
"inspect",
|
|
212
|
+
"head-sha",
|
|
213
|
+
"lint",
|
|
214
|
+
"repo-log",
|
|
215
|
+
"repo-diff",
|
|
216
|
+
"divergence-status",
|
|
217
|
+
"require-fresh-llm-context",
|
|
218
|
+
"verify-llm-context-refresh",
|
|
219
|
+
"command-reference-check",
|
|
220
|
+
"command-taxonomy-check",
|
|
221
|
+
"standard-gates-audit-suite",
|
|
222
|
+
"protected-diff-plan",
|
|
223
|
+
"post-merge-check",
|
|
224
|
+
"command-for",
|
|
225
|
+
"refresher",
|
|
226
|
+
"render-md",
|
|
227
|
+
"session-start",
|
|
228
|
+
}
|
|
229
|
+
read_only_prefixes = (
|
|
230
|
+
"agentic-kit audit-",
|
|
231
|
+
"agentic-kit direction audit-",
|
|
232
|
+
"agentic-kit docs lifecycle plan",
|
|
233
|
+
"agentic-kit docs lifecycle triage",
|
|
234
|
+
"agentic-kit docs removed-source-audit",
|
|
235
|
+
"agentic-kit governance check",
|
|
236
|
+
"agentic-kit actions list",
|
|
237
|
+
"agentic-kit actions show",
|
|
238
|
+
"agentic-kit cockpit status",
|
|
239
|
+
"agentic-kit cockpit gatekeeper-status",
|
|
240
|
+
"agentic-kit cockpit actions",
|
|
241
|
+
)
|
|
242
|
+
if leaf in read_only_leafs or qualified.startswith(read_only_prefixes):
|
|
243
|
+
return "READ_ONLY"
|
|
244
|
+
if leaf.startswith("audit-") or leaf.endswith("-audit") or leaf.startswith("validate"):
|
|
245
|
+
return "READ_ONLY"
|
|
246
|
+
if group in {"actions", "patterns"} and leaf in {"list", "show"}:
|
|
247
|
+
return "READ_ONLY"
|
|
248
|
+
|
|
249
|
+
return "BOUNDED"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def infer_surface(command: dict[str, Any]) -> str:
|
|
253
|
+
qualified = str(command.get("qualified_name") or "")
|
|
254
|
+
path = command.get("path")
|
|
255
|
+
path_parts = [str(part) for part in path] if isinstance(path, list) else []
|
|
256
|
+
group = str(command.get("group") or (path_parts[0] if path_parts else "root"))
|
|
257
|
+
leaf = _leaf(command)
|
|
258
|
+
|
|
259
|
+
orchestrator_commands = {
|
|
260
|
+
"agentic-kit artifact-gc",
|
|
261
|
+
"agentic-kit chat session-start",
|
|
262
|
+
"agentic-kit docs lifecycle sweep",
|
|
263
|
+
"agentic-kit dpa final-closeout-check",
|
|
264
|
+
"agentic-kit github-create",
|
|
265
|
+
"agentic-kit init",
|
|
266
|
+
"agentic-kit post-release-doi-closeout",
|
|
267
|
+
"agentic-kit release prepare",
|
|
268
|
+
"agentic-kit release ready",
|
|
269
|
+
"agentic-kit release-prep",
|
|
270
|
+
"agentic-kit release-publish",
|
|
271
|
+
"agentic-kit transfer admin-refresh-pr",
|
|
272
|
+
"agentic-kit transfer chat-switch-complete",
|
|
273
|
+
"agentic-kit transfer evidence-pr-complete",
|
|
274
|
+
"agentic-kit transfer post-merge-complete",
|
|
275
|
+
"agentic-kit transfer post-merge-settle",
|
|
276
|
+
"agentic-kit transfer pr-closeout-complete",
|
|
277
|
+
"agentic-kit transfer pr-complete",
|
|
278
|
+
"agentic-kit transfer pr-create-complete",
|
|
279
|
+
"agentic-kit transfer remote-next",
|
|
280
|
+
"agentic-kit transfer remote-work-start",
|
|
281
|
+
"agentic-kit transfer sync-main",
|
|
282
|
+
"agentic-kit work finish",
|
|
283
|
+
"agentic-kit work recover",
|
|
284
|
+
"agentic-kit work start",
|
|
285
|
+
"agentic-kit workflow go",
|
|
286
|
+
"agentic-kit workspace adopt",
|
|
287
|
+
"agentic-kit workspace dpa-intake",
|
|
288
|
+
"agentic-kit workspace init",
|
|
289
|
+
"agentic-kit workspace remove",
|
|
290
|
+
"agentic-kit workspace upgrade",
|
|
291
|
+
}
|
|
292
|
+
if qualified in orchestrator_commands:
|
|
293
|
+
return "orchestrator"
|
|
294
|
+
|
|
295
|
+
primitive_exact = {
|
|
296
|
+
"agentic-kit transfer publish-last-report",
|
|
297
|
+
}
|
|
298
|
+
diagnostic_leaf_terms = (
|
|
299
|
+
"audit",
|
|
300
|
+
"check",
|
|
301
|
+
"status",
|
|
302
|
+
"inspect",
|
|
303
|
+
"list",
|
|
304
|
+
"show",
|
|
305
|
+
"validate",
|
|
306
|
+
"readiness",
|
|
307
|
+
"doctor",
|
|
308
|
+
"render",
|
|
309
|
+
"report",
|
|
310
|
+
"summary",
|
|
311
|
+
"diff",
|
|
312
|
+
"plan",
|
|
313
|
+
"diagnose",
|
|
314
|
+
"classify",
|
|
315
|
+
)
|
|
316
|
+
diagnostic_exact = {
|
|
317
|
+
"agentic-kit check",
|
|
318
|
+
"agentic-kit check-docs",
|
|
319
|
+
"agentic-kit check-todo",
|
|
320
|
+
"agentic-kit command-for",
|
|
321
|
+
}
|
|
322
|
+
if qualified in primitive_exact:
|
|
323
|
+
return "primitive"
|
|
324
|
+
if qualified in diagnostic_exact:
|
|
325
|
+
return "diagnostic"
|
|
326
|
+
if any(term in leaf for term in diagnostic_leaf_terms):
|
|
327
|
+
return "diagnostic"
|
|
328
|
+
if group == "dpa" and any(term in qualified for term in ("assessment", "closeout", "stage", "gate")):
|
|
329
|
+
return "diagnostic"
|
|
330
|
+
if infer_safety(command) == "READ_ONLY" and group not in {"workspace"}:
|
|
331
|
+
return "diagnostic"
|
|
332
|
+
return "primitive"
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _task_tags(command: dict[str, Any]) -> list[str]:
|
|
336
|
+
tags = []
|
|
337
|
+
group = str(command.get("group") or "")
|
|
338
|
+
if group and group != "root":
|
|
339
|
+
tags.append(group.replace(" ", "-"))
|
|
340
|
+
safety = str(command.get("safety") or infer_safety(command)).lower()
|
|
341
|
+
tags.append(safety.replace("_", "-"))
|
|
342
|
+
return sorted(dict.fromkeys(tags))
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _when_to_use(command: dict[str, Any]) -> str:
|
|
346
|
+
help_text = str(command.get("help") or "").strip()
|
|
347
|
+
if help_text:
|
|
348
|
+
return help_text.splitlines()[0].strip()
|
|
349
|
+
return f"Run {command.get('qualified_name')}."
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _dry_run_available(command: dict[str, Any]) -> bool:
|
|
353
|
+
opts = _all_opts(command)
|
|
354
|
+
return "--dry-run" in opts or "--execute" in opts
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _with_manifest_fields(command: dict[str, Any]) -> dict[str, Any]:
|
|
358
|
+
enriched = dict(command)
|
|
359
|
+
qualified_name = str(enriched.get("qualified_name") or "")
|
|
360
|
+
enriched["safety"] = str(enriched.get("safety") or infer_safety(enriched))
|
|
361
|
+
enriched["surface"] = str(enriched.get("surface") or infer_surface(enriched))
|
|
362
|
+
enriched["task_tags"] = list(enriched.get("task_tags") or _task_tags(enriched))
|
|
363
|
+
enriched["when_to_use"] = str(enriched.get("when_to_use") or _when_to_use(enriched))
|
|
364
|
+
if qualified_name in LIFECYCLE_RANKS:
|
|
365
|
+
enriched["lifecycle_rank"] = LIFECYCLE_RANKS[qualified_name]
|
|
366
|
+
enriched["replaces_raw"] = list(
|
|
367
|
+
enriched.get("replaces_raw") or RAW_REPLACEMENTS.get(qualified_name, ())
|
|
368
|
+
)
|
|
369
|
+
enriched["dry_run_available"] = bool(
|
|
370
|
+
enriched.get("dry_run_available")
|
|
371
|
+
if "dry_run_available" in enriched
|
|
372
|
+
else _dry_run_available(enriched)
|
|
373
|
+
)
|
|
374
|
+
return enriched
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def build_reference_from_app(app: Any) -> dict[str, Any]:
|
|
378
|
+
root = get_command(app)
|
|
379
|
+
commands: list[dict[str, Any]] = []
|
|
380
|
+
if not hasattr(root, "commands"):
|
|
381
|
+
command_name = str(getattr(root, "name", None) or "root")
|
|
382
|
+
commands.append(_record_for([command_name], root))
|
|
383
|
+
sha = manifest_sha(commands)
|
|
384
|
+
return {
|
|
385
|
+
"schema_version": 2,
|
|
386
|
+
"kind": "agentic_kit_command_reference",
|
|
387
|
+
"source": "generated_from_typer_click_registry",
|
|
388
|
+
"generated_by": "scripts/generate_agentic_kit_command_reference.py",
|
|
389
|
+
"successor_execution_contract": "docs/reports/handoff-packages/latest/execution_contract.json",
|
|
390
|
+
"meta": {
|
|
391
|
+
"schema_version": 1,
|
|
392
|
+
"manifest_sha": sha,
|
|
393
|
+
"generated_md": MD_PATH.as_posix(),
|
|
394
|
+
},
|
|
395
|
+
"commands": commands,
|
|
396
|
+
}
|
|
397
|
+
for name, command in sorted(root.commands.items()):
|
|
398
|
+
nested = getattr(command, "commands", None)
|
|
399
|
+
if isinstance(nested, dict):
|
|
400
|
+
commands.extend(_walk([name], command))
|
|
401
|
+
else:
|
|
402
|
+
commands.append(_record_for([name], command))
|
|
403
|
+
|
|
404
|
+
commands = sorted(commands, key=lambda item: item["qualified_name"])
|
|
405
|
+
sha = manifest_sha(commands)
|
|
406
|
+
return {
|
|
407
|
+
"schema_version": 2,
|
|
408
|
+
"kind": "agentic_kit_command_reference",
|
|
409
|
+
"source": "generated_from_typer_click_registry",
|
|
410
|
+
"generated_by": "scripts/generate_agentic_kit_command_reference.py",
|
|
411
|
+
"successor_execution_contract": "docs/reports/handoff-packages/latest/execution_contract.json",
|
|
412
|
+
"meta": {
|
|
413
|
+
"schema_version": 1,
|
|
414
|
+
"manifest_sha": sha,
|
|
415
|
+
"generated_md": MD_PATH.as_posix(),
|
|
416
|
+
},
|
|
417
|
+
"commands": commands,
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def build_current_reference() -> dict[str, Any]:
|
|
422
|
+
from agentic_project_kit.cli import app
|
|
423
|
+
|
|
424
|
+
return build_reference_from_app(app)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def render_markdown(data: dict[str, Any]) -> str:
|
|
428
|
+
commands = data.get("commands") or []
|
|
429
|
+
meta = data.get("meta") if isinstance(data.get("meta"), dict) else {}
|
|
430
|
+
sha = str(meta.get("manifest_sha") or manifest_sha(commands))
|
|
431
|
+
lines = [
|
|
432
|
+
"# Agentic-kit command reference",
|
|
433
|
+
"",
|
|
434
|
+
f"GENERATED FROM agentic-kit-commands.json — do not edit; manifest_sha: {sha}",
|
|
435
|
+
"",
|
|
436
|
+
"> Successor handoff contract note: the machine-readable successor execution contract is written to `docs/reports/handoff-packages/latest/execution_contract.json`. This generated command reference points to the contract instead of duplicating local-command rules.",
|
|
437
|
+
"",
|
|
438
|
+
f"- Schema version: `{data['schema_version']}`",
|
|
439
|
+
f"- Source: `{data['source']}`",
|
|
440
|
+
f"- Command count: `{len(commands)}`",
|
|
441
|
+
"",
|
|
442
|
+
"## Commands",
|
|
443
|
+
"",
|
|
444
|
+
]
|
|
445
|
+
for command in commands:
|
|
446
|
+
lines.append(f"### `{command['qualified_name']}`")
|
|
447
|
+
lines.append("")
|
|
448
|
+
lines.append(f"- Safety: `{command.get('safety', '')}`")
|
|
449
|
+
lines.append(f"- Surface: `{command.get('surface', '')}`")
|
|
450
|
+
lines.append(f"- When to use: {command.get('when_to_use', '')}")
|
|
451
|
+
lines.append(f"- Dry-run available: `{bool(command.get('dry_run_available'))}`")
|
|
452
|
+
replaces = command.get("replaces_raw") or []
|
|
453
|
+
if replaces:
|
|
454
|
+
lines.append("- Replaces raw: " + ", ".join(f"`{item}`" for item in replaces))
|
|
455
|
+
lines.append("")
|
|
456
|
+
if command.get("help"):
|
|
457
|
+
lines.append(str(command["help"]).strip())
|
|
458
|
+
lines.append("")
|
|
459
|
+
if command.get("params"):
|
|
460
|
+
lines.append("| Parameter | Type | Options | Required | Default | Help |")
|
|
461
|
+
lines.append("|---|---:|---|---:|---|---|")
|
|
462
|
+
for param in command["params"]:
|
|
463
|
+
opts = ", ".join(param.get("opts", []) + param.get("secondary_opts", []))
|
|
464
|
+
default = "" if param.get("default") is None else f"`{param.get('default')}`"
|
|
465
|
+
help_text = (param.get("help") or "").replace("\n", " ")
|
|
466
|
+
lines.append(
|
|
467
|
+
f"| `{param.get('name')}` | `{param.get('param_type')}` | "
|
|
468
|
+
f"{opts} | `{param.get('required')}` | {default} | {help_text} |"
|
|
469
|
+
)
|
|
470
|
+
lines.append("")
|
|
471
|
+
else:
|
|
472
|
+
lines.append("_No parameters._")
|
|
473
|
+
lines.append("")
|
|
474
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def write_reference(root: Path) -> dict[str, Any]:
|
|
478
|
+
data = build_current_reference()
|
|
479
|
+
json_path = load_workspace(root).reference_file("agentic-kit-commands.json")
|
|
480
|
+
md_path = load_workspace(root).reference_file("AGENTIC_KIT_COMMANDS.md")
|
|
481
|
+
json_path.parent.mkdir(parents=True, exist_ok=True)
|
|
482
|
+
json_path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
483
|
+
md_path.write_text(render_markdown(data), encoding="utf-8")
|
|
484
|
+
return data
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def load_manifest(root: Path) -> dict[str, Any]:
|
|
488
|
+
return json.loads(load_workspace(root).reference_file("agentic-kit-commands.json").read_text(encoding="utf-8"))
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def evaluate_command_manifest(root: Path = Path(".")) -> CommandManifestAudit:
|
|
492
|
+
root = root.resolve()
|
|
493
|
+
findings: list[CommandManifestFinding] = []
|
|
494
|
+
json_path = load_workspace(root).reference_file("agentic-kit-commands.json")
|
|
495
|
+
md_path = load_workspace(root).reference_file("AGENTIC_KIT_COMMANDS.md")
|
|
496
|
+
|
|
497
|
+
try:
|
|
498
|
+
committed = load_manifest(root)
|
|
499
|
+
except Exception as exc:
|
|
500
|
+
return CommandManifestAudit(
|
|
501
|
+
root=root.as_posix(),
|
|
502
|
+
findings=(CommandManifestFinding("BLOCK", "JSON_UNREADABLE", str(exc)),),
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
commands = committed.get("commands")
|
|
506
|
+
if not isinstance(commands, list):
|
|
507
|
+
findings.append(CommandManifestFinding("BLOCK", "COMMANDS_NOT_LIST", "commands is not a list"))
|
|
508
|
+
commands = []
|
|
509
|
+
|
|
510
|
+
current = build_current_reference()
|
|
511
|
+
committed_names = {str(item.get("qualified_name")) for item in commands if isinstance(item, dict)}
|
|
512
|
+
current_names = {
|
|
513
|
+
str(item.get("qualified_name"))
|
|
514
|
+
for item in current.get("commands", [])
|
|
515
|
+
if isinstance(item, dict)
|
|
516
|
+
}
|
|
517
|
+
for name in sorted(current_names - committed_names):
|
|
518
|
+
findings.append(CommandManifestFinding("BLOCK", "CLI_WITHOUT_JSON", name))
|
|
519
|
+
for name in sorted(committed_names - current_names):
|
|
520
|
+
findings.append(CommandManifestFinding("BLOCK", "JSON_WITHOUT_CLI", name))
|
|
521
|
+
|
|
522
|
+
for command in commands:
|
|
523
|
+
if not isinstance(command, dict):
|
|
524
|
+
findings.append(CommandManifestFinding("BLOCK", "COMMAND_NOT_OBJECT", repr(command)))
|
|
525
|
+
continue
|
|
526
|
+
qualified = str(command.get("qualified_name") or "<unknown>")
|
|
527
|
+
safety = command.get("safety")
|
|
528
|
+
if safety not in SAFETY_VALUES:
|
|
529
|
+
findings.append(
|
|
530
|
+
CommandManifestFinding("BLOCK", "SAFETY_INVALID", f"{qualified}: {safety!r}")
|
|
531
|
+
)
|
|
532
|
+
surface = command.get("surface")
|
|
533
|
+
if surface not in SURFACE_VALUES:
|
|
534
|
+
findings.append(
|
|
535
|
+
CommandManifestFinding("BLOCK", "SURFACE_INVALID", f"{qualified}: {surface!r}")
|
|
536
|
+
)
|
|
537
|
+
replaces_raw = command.get("replaces_raw", [])
|
|
538
|
+
if replaces_raw is None:
|
|
539
|
+
replaces_raw = []
|
|
540
|
+
if not isinstance(replaces_raw, list) or any(not str(item).strip() for item in replaces_raw):
|
|
541
|
+
findings.append(
|
|
542
|
+
CommandManifestFinding("BLOCK", "REPLACES_RAW_INVALID", f"{qualified}: {replaces_raw!r}")
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
meta = committed.get("meta") if isinstance(committed.get("meta"), dict) else {}
|
|
546
|
+
expected_sha = manifest_sha(commands)
|
|
547
|
+
if meta.get("manifest_sha") != expected_sha:
|
|
548
|
+
findings.append(
|
|
549
|
+
CommandManifestFinding(
|
|
550
|
+
"BLOCK",
|
|
551
|
+
"MANIFEST_SHA_MISMATCH",
|
|
552
|
+
f"expected {expected_sha}, found {meta.get('manifest_sha')!r}",
|
|
553
|
+
)
|
|
554
|
+
)
|
|
555
|
+
if meta.get("generated_md") != MD_PATH.as_posix():
|
|
556
|
+
findings.append(
|
|
557
|
+
CommandManifestFinding(
|
|
558
|
+
"BLOCK",
|
|
559
|
+
"GENERATED_MD_MISMATCH",
|
|
560
|
+
f"expected {MD_PATH.as_posix()}, found {meta.get('generated_md')!r}",
|
|
561
|
+
)
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
if committed != current:
|
|
565
|
+
findings.append(
|
|
566
|
+
CommandManifestFinding("BLOCK", "JSON_DRIFT", f"{json_path.as_posix()} differs from CLI")
|
|
567
|
+
)
|
|
568
|
+
expected_md = render_markdown(committed)
|
|
569
|
+
actual_md = md_path.read_text(encoding="utf-8") if md_path.exists() else ""
|
|
570
|
+
if actual_md != expected_md:
|
|
571
|
+
findings.append(
|
|
572
|
+
CommandManifestFinding("BLOCK", "MD_DRIFT", f"{md_path.as_posix()} differs from generator")
|
|
573
|
+
)
|
|
574
|
+
from agentic_project_kit.chat_entrypoint_contract import audit_entrypoint_manifest_references
|
|
575
|
+
|
|
576
|
+
for finding in audit_entrypoint_manifest_references(root, committed):
|
|
577
|
+
findings.append(CommandManifestFinding("BLOCK", finding["code"], finding["message"]))
|
|
578
|
+
|
|
579
|
+
return CommandManifestAudit(root=root.as_posix(), findings=tuple(findings))
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def render_command_manifest_audit(audit: CommandManifestAudit) -> str:
|
|
583
|
+
lines = [
|
|
584
|
+
"COMMAND_MANIFEST_AUDIT",
|
|
585
|
+
f"STATUS={audit.status}",
|
|
586
|
+
f"FINDING_COUNT={len(audit.findings)}",
|
|
587
|
+
f"BLOCKER_COUNT={len(audit.blockers)}",
|
|
588
|
+
]
|
|
589
|
+
for finding in audit.findings:
|
|
590
|
+
lines.append(f"FINDING={finding.severity}|{finding.code}|{finding.message}")
|
|
591
|
+
return "\n".join(lines) + "\n"
|