loopx 0.4.8__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.
- loopx/__init__.py +5 -0
- loopx/agent_onboarding.py +654 -0
- loopx/agent_registry.py +112 -0
- loopx/ark_managed_agent_host.py +59 -0
- loopx/authority.py +805 -0
- loopx/benchmark.py +2875 -0
- loopx/benchmark_adapters/__init__.py +1 -0
- loopx/benchmark_adapters/agentissue.py +2644 -0
- loopx/benchmark_adapters/agents_last_exam.py +3998 -0
- loopx/benchmark_adapters/edgebench.py +322 -0
- loopx/benchmark_adapters/skillsbench.py +5978 -0
- loopx/benchmark_adapters/skillsbench_acp_failure_policy.py +143 -0
- loopx/benchmark_adapters/skillsbench_acp_process.py +31 -0
- loopx/benchmark_adapters/skillsbench_acp_relay.py +4832 -0
- loopx/benchmark_adapters/skillsbench_batch.py +124 -0
- loopx/benchmark_adapters/skillsbench_bridge_guard.py +209 -0
- loopx/benchmark_adapters/skillsbench_bridge_summary.py +203 -0
- loopx/benchmark_adapters/skillsbench_codex_goal_recovery.py +271 -0
- loopx/benchmark_adapters/skillsbench_codex_goal_trace.py +81 -0
- loopx/benchmark_adapters/skillsbench_codex_runtime.py +339 -0
- loopx/benchmark_adapters/skillsbench_dockerfile_runtime.py +467 -0
- loopx/benchmark_adapters/skillsbench_failure_signals.py +652 -0
- loopx/benchmark_adapters/skillsbench_proxy_runtime.py +327 -0
- loopx/benchmark_adapters/skillsbench_remote_bridge.py +402 -0
- loopx/benchmark_adapters/skillsbench_result_discovery.py +143 -0
- loopx/benchmark_adapters/skillsbench_runner_profile.py +436 -0
- loopx/benchmark_adapters/skillsbench_runner_source.py +99 -0
- loopx/benchmark_adapters/skillsbench_setup_preflight.py +771 -0
- loopx/benchmark_adapters/skillsbench_signals.py +15 -0
- loopx/benchmark_adapters/skillsbench_task_source.py +141 -0
- loopx/benchmark_adapters/skillsbench_turn_route.py +723 -0
- loopx/benchmark_adapters/skillsbench_turn_runtime.py +1069 -0
- loopx/benchmark_adapters/skillsbench_typed_repair.py +689 -0
- loopx/benchmark_adapters/skillsbench_uv_cache.py +111 -0
- loopx/benchmark_adapters/skillsbench_verifier_bootstrap.py +227 -0
- loopx/benchmark_adapters/skillsbench_verifier_cache.py +138 -0
- loopx/benchmark_adapters/terminal_bench.py +10078 -0
- loopx/benchmark_case_analysis.py +1276 -0
- loopx/benchmark_case_state.py +1079 -0
- loopx/benchmark_core/__init__.py +239 -0
- loopx/benchmark_core/adapter.py +84 -0
- loopx/benchmark_core/artifacts.py +517 -0
- loopx/benchmark_core/attempts.py +199 -0
- loopx/benchmark_core/container_exec.py +216 -0
- loopx/benchmark_core/io.py +68 -0
- loopx/benchmark_core/lifecycle.py +211 -0
- loopx/benchmark_core/loop_protocol.py +689 -0
- loopx/benchmark_core/observable_handles.py +348 -0
- loopx/benchmark_core/parity.py +256 -0
- loopx/benchmark_core/remote_closeout.py +482 -0
- loopx/benchmark_core/rounds.py +215 -0
- loopx/benchmark_core/route_profile.py +509 -0
- loopx/benchmark_core/run_permissions.py +206 -0
- loopx/benchmark_core/split_control.py +925 -0
- loopx/benchmark_core/turn_fidelity.py +326 -0
- loopx/benchmark_ledger.py +3793 -0
- loopx/benchmark_ledger_countability.py +372 -0
- loopx/benchmark_ledger_current.py +724 -0
- loopx/benchmark_trajectory.py +405 -0
- loopx/benchmarks/__init__.py +1 -0
- loopx/benchmarks/qualification/__init__.py +1 -0
- loopx/benchmarks/qualification/release_outcome_baseline.py +360 -0
- loopx/benchmarks/read_models/__init__.py +1 -0
- loopx/benchmarks/read_models/benchmark_attempt_accounting.py +53 -0
- loopx/benchmarks/read_models/benchmark_comparison.py +414 -0
- loopx/benchmarks/read_models/benchmark_event_timeline.py +113 -0
- loopx/benchmarks/read_models/benchmark_experiment_report.py +475 -0
- loopx/benchmarks/read_models/benchmark_learning_ledger.py +137 -0
- loopx/benchmarks/read_models/benchmark_lifecycle_contracts.py +228 -0
- loopx/benchmarks/read_models/benchmark_projection.py +723 -0
- loopx/benchmarks/read_models/benchmark_result.py +146 -0
- loopx/benchmarks/read_models/benchmark_run_execution_contract.py +116 -0
- loopx/benchmarks/read_models/benchmark_run_failure.py +157 -0
- loopx/benchmarks/read_models/benchmark_run_metrics.py +213 -0
- loopx/benchmarks/read_models/benchmark_run_post_execution.py +635 -0
- loopx/benchmarks/read_models/benchmark_run_pre_execution.py +541 -0
- loopx/benchmarks/read_models/benchmark_status_compaction.py +1255 -0
- loopx/benchmarks/read_models/benchmark_status_runner.py +780 -0
- loopx/benchmarks/read_models/goal_start_control_score.py +857 -0
- loopx/benchmarks/read_models/skillsbench_post_run_debug.py +746 -0
- loopx/benchmarks/read_models/skillsbench_verifier_attribution.py +269 -0
- loopx/bootstrap.py +1116 -0
- loopx/bootstrap_command_pack.py +2167 -0
- loopx/boundary_authority.py +199 -0
- loopx/canary/__init__.py +1 -0
- loopx/canary/maintainability_ratchet.py +800 -0
- loopx/canary/planner.py +1984 -0
- loopx/canary/premerge.py +1130 -0
- loopx/canary/qualification_profiles.py +309 -0
- loopx/canary/quality_surface_catalog.py +838 -0
- loopx/canary/release_profiles.py +51 -0
- loopx/canary/runner.py +1107 -0
- loopx/canary/smoke_health.py +581 -0
- loopx/canary/smoke_profiles.py +212 -0
- loopx/capabilities/__init__.py +0 -0
- loopx/capabilities/agent_turn_recall/__init__.py +17 -0
- loopx/capabilities/agent_turn_recall/cli.py +369 -0
- loopx/capabilities/agent_turn_recall/core.py +296 -0
- loopx/capabilities/auto_research/__init__.py +16 -0
- loopx/capabilities/auto_research/bootstrap_contract.py +157 -0
- loopx/capabilities/auto_research/cli.py +1468 -0
- loopx/capabilities/auto_research/core.py +11 -0
- loopx/capabilities/auto_research/defaults.py +79 -0
- loopx/capabilities/auto_research/demo_e2e.py +1848 -0
- loopx/capabilities/auto_research/demo_supervisor.py +186 -0
- loopx/capabilities/auto_research/evidence_packet.py +767 -0
- loopx/capabilities/auto_research/human_view.py +794 -0
- loopx/capabilities/auto_research/kernel.py +191 -0
- loopx/capabilities/auto_research/knn_demo_workspace.py +322 -0
- loopx/capabilities/auto_research/live_evidence.py +248 -0
- loopx/capabilities/auto_research/preset.py +176 -0
- loopx/capabilities/auto_research/research_state.py +1085 -0
- loopx/capabilities/auto_research/role_profiles.py +394 -0
- loopx/capabilities/auto_research/rollout_append.py +97 -0
- loopx/capabilities/auto_research/terminal_result_contract.py +422 -0
- loopx/capabilities/auto_research/terminal_result_projection.py +171 -0
- loopx/capabilities/auto_research/terminal_result_query.py +233 -0
- loopx/capabilities/auto_research/terminal_results.py +349 -0
- loopx/capabilities/auto_research/user_contract.py +190 -0
- loopx/capabilities/auto_research/worker_loop.py +163 -0
- loopx/capabilities/auto_research/worker_runtime.py +777 -0
- loopx/capabilities/auto_research/worker_skill/SKILL.md +343 -0
- loopx/capabilities/benchmark_toolkit/__init__.py +19 -0
- loopx/capabilities/benchmark_toolkit/integrity.py +387 -0
- loopx/capabilities/catalog.py +1875 -0
- loopx/capabilities/change_quality/__init__.py +19 -0
- loopx/capabilities/change_quality/cli.py +171 -0
- loopx/capabilities/change_quality/context.py +156 -0
- loopx/capabilities/change_quality/oracles.py +269 -0
- loopx/capabilities/change_quality/policy.py +34 -0
- loopx/capabilities/change_quality/receipt.py +482 -0
- loopx/capabilities/change_quality/result.py +493 -0
- loopx/capabilities/change_quality/scope.py +171 -0
- loopx/capabilities/change_quality/shadow.py +680 -0
- loopx/capabilities/content_ops/__init__.py +0 -0
- loopx/capabilities/content_ops/cli.py +649 -0
- loopx/capabilities/content_ops/connector_packets.py +164 -0
- loopx/capabilities/content_ops/item_lifecycle.py +1000 -0
- loopx/capabilities/content_ops/layout.py +451 -0
- loopx/capabilities/content_ops/markdown.py +456 -0
- loopx/capabilities/content_ops/schemas.py +51 -0
- loopx/capabilities/content_ops/social_browser_x.py +107 -0
- loopx/capabilities/content_ops/surface.py +1956 -0
- loopx/capabilities/content_ops/templates/layout-catalog-v0.json +72 -0
- loopx/capabilities/context_providers/__init__.py +36 -0
- loopx/capabilities/context_providers/base.py +189 -0
- loopx/capabilities/context_providers/factory.py +32 -0
- loopx/capabilities/context_providers/openviking.py +702 -0
- loopx/capabilities/context_providers/service_ownership.py +185 -0
- loopx/capabilities/decision_context/__init__.py +129 -0
- loopx/capabilities/decision_context/architecture.py +83 -0
- loopx/capabilities/decision_context/assembler.py +849 -0
- loopx/capabilities/decision_context/catalog_entry.py +195 -0
- loopx/capabilities/decision_context/cli.py +310 -0
- loopx/capabilities/decision_context/cursor_commit.py +535 -0
- loopx/capabilities/decision_context/outcome_feedback.py +352 -0
- loopx/capabilities/decision_context/packets.py +654 -0
- loopx/capabilities/decision_context/private_state.py +189 -0
- loopx/capabilities/decision_context/profile.py +453 -0
- loopx/capabilities/decision_context/providers.py +228 -0
- loopx/capabilities/decision_context/review_settlement.py +136 -0
- loopx/capabilities/decision_context/runtime.py +273 -0
- loopx/capabilities/decision_context/sources.py +415 -0
- loopx/capabilities/explore/__init__.py +1 -0
- loopx/capabilities/explore/activation.py +198 -0
- loopx/capabilities/explore/adaptive_replay_planner.py +221 -0
- loopx/capabilities/explore/child_replay_runtime.py +463 -0
- loopx/capabilities/explore/composition_frontier.py +291 -0
- loopx/capabilities/explore/counterfactual_runtime.py +578 -0
- loopx/capabilities/explore/episode_runtime.py +647 -0
- loopx/capabilities/explore/harness_checkpoint.py +171 -0
- loopx/capabilities/explore/harness_gate.py +115 -0
- loopx/capabilities/explore/harness_runtime.py +1124 -0
- loopx/capabilities/explore/replay_metrics.py +206 -0
- loopx/capabilities/explore/replay_runtime.py +1271 -0
- loopx/capabilities/explore/resource_portfolio.py +173 -0
- loopx/capabilities/explore/result_log.py +974 -0
- loopx/capabilities/explore/router_state.py +432 -0
- loopx/capabilities/explore/source_history_reconcile.py +255 -0
- loopx/capabilities/explore/speculative_scheduler.py +498 -0
- loopx/capabilities/explore/todo_branch_plan.py +650 -0
- loopx/capabilities/explore/todo_evidence.py +141 -0
- loopx/capabilities/explore/trace_runtime.py +284 -0
- loopx/capabilities/explore/worker_branch_plan.py +1257 -0
- loopx/capabilities/integration_branch/__init__.py +13 -0
- loopx/capabilities/integration_branch/cli.py +148 -0
- loopx/capabilities/integration_branch/core.py +916 -0
- loopx/capabilities/issue_fix/__init__.py +19 -0
- loopx/capabilities/issue_fix/acceptance_loop.py +1050 -0
- loopx/capabilities/issue_fix/candidate_evidence.py +503 -0
- loopx/capabilities/issue_fix/candidate_preflight.py +676 -0
- loopx/capabilities/issue_fix/cli.py +1822 -0
- loopx/capabilities/issue_fix/cli_input.py +87 -0
- loopx/capabilities/issue_fix/content_ops_cli.py +148 -0
- loopx/capabilities/issue_fix/discovered_issue_promotion.py +947 -0
- loopx/capabilities/issue_fix/explore_projection.py +710 -0
- loopx/capabilities/issue_fix/feasibility.py +542 -0
- loopx/capabilities/issue_fix/github_public.py +661 -0
- loopx/capabilities/issue_fix/intake_surface.py +832 -0
- loopx/capabilities/issue_fix/metadata_preview.py +218 -0
- loopx/capabilities/issue_fix/metrics_projection.py +1340 -0
- loopx/capabilities/issue_fix/metrics_supplement.py +634 -0
- loopx/capabilities/issue_fix/metrics_supplement_cli.py +127 -0
- loopx/capabilities/issue_fix/outcome_projection.py +1235 -0
- loopx/capabilities/issue_fix/periodic_report.py +189 -0
- loopx/capabilities/issue_fix/pr_description.py +418 -0
- loopx/capabilities/issue_fix/pr_gate_reconcile.py +496 -0
- loopx/capabilities/issue_fix/pr_gate_reconcile_cli.py +464 -0
- loopx/capabilities/issue_fix/pr_lifecycle.py +1327 -0
- loopx/capabilities/issue_fix/pr_lifecycle_rollout.py +85 -0
- loopx/capabilities/issue_fix/pr_monitor_materialization.py +257 -0
- loopx/capabilities/issue_fix/pr_review_ack.py +439 -0
- loopx/capabilities/issue_fix/provider_hooks.py +24 -0
- loopx/capabilities/issue_fix/repository_commit_evidence.py +186 -0
- loopx/capabilities/issue_fix/repository_context.py +457 -0
- loopx/capabilities/issue_fix/repository_memory.py +459 -0
- loopx/capabilities/issue_fix/repository_memory_provider.py +1454 -0
- loopx/capabilities/issue_fix/repository_snapshot.py +454 -0
- loopx/capabilities/issue_fix/reviewer_cli.py +917 -0
- loopx/capabilities/issue_fix/reviewer_notification.py +882 -0
- loopx/capabilities/issue_fix/reviewer_notification_drain.py +942 -0
- loopx/capabilities/issue_fix/reviewer_recommendation.py +1057 -0
- loopx/capabilities/issue_fix/reviewer_request.py +1282 -0
- loopx/capabilities/issue_fix/reward_memory.py +879 -0
- loopx/capabilities/issue_fix/workflow_plan.py +1286 -0
- loopx/capabilities/material_lifecycle/__init__.py +161 -0
- loopx/capabilities/material_lifecycle/_validation.py +183 -0
- loopx/capabilities/material_lifecycle/apply.py +672 -0
- loopx/capabilities/material_lifecycle/architecture.py +122 -0
- loopx/capabilities/material_lifecycle/cli.py +161 -0
- loopx/capabilities/material_lifecycle/decision_planning.py +470 -0
- loopx/capabilities/material_lifecycle/explore_execution.py +306 -0
- loopx/capabilities/material_lifecycle/intake.py +869 -0
- loopx/capabilities/material_lifecycle/inventory.py +147 -0
- loopx/capabilities/material_lifecycle/lifecycle.py +98 -0
- loopx/capabilities/material_lifecycle/preparation.py +147 -0
- loopx/capabilities/material_lifecycle/project_skill.py +83 -0
- loopx/capabilities/material_lifecycle/ranking.py +267 -0
- loopx/capabilities/material_lifecycle/readable_projection.py +500 -0
- loopx/capabilities/material_lifecycle/rebuild.py +480 -0
- loopx/capabilities/material_lifecycle/settlement.py +238 -0
- loopx/capabilities/periodic_report/__init__.py +71 -0
- loopx/capabilities/periodic_report/adapters.py +939 -0
- loopx/capabilities/periodic_report/archive.py +422 -0
- loopx/capabilities/periodic_report/bindings.py +705 -0
- loopx/capabilities/periodic_report/cli.py +277 -0
- loopx/capabilities/periodic_report/core.py +691 -0
- loopx/capabilities/periodic_report/extension_envelope.py +66 -0
- loopx/capabilities/periodic_report/presets.py +103 -0
- loopx/capabilities/periodic_report/profile.py +235 -0
- loopx/capabilities/periodic_report/project_progress.py +179 -0
- loopx/capabilities/periodic_report/triggers.py +452 -0
- loopx/capabilities/pr_review_queue/__init__.py +17 -0
- loopx/capabilities/pr_review_queue/core.py +506 -0
- loopx/capabilities/pr_review_queue/review_contract.py +506 -0
- loopx/capabilities/registry.py +192 -0
- loopx/capabilities/reward_memory/__init__.py +75 -0
- loopx/capabilities/reward_memory/application.py +819 -0
- loopx/capabilities/reward_memory/architecture.py +572 -0
- loopx/capabilities/reward_memory/candidate_review.py +511 -0
- loopx/capabilities/reward_memory/cli.py +469 -0
- loopx/capabilities/reward_memory/dogfood.py +574 -0
- loopx/capabilities/reward_memory/evaluation.py +296 -0
- loopx/capabilities/reward_memory/evaluation_fixtures.py +362 -0
- loopx/capabilities/reward_memory/experiment.py +567 -0
- loopx/capabilities/reward_memory/health.py +222 -0
- loopx/capabilities/reward_memory/ingestion.py +519 -0
- loopx/capabilities/reward_memory/registry.py +600 -0
- loopx/capabilities/reward_memory/runtime_hooks.py +312 -0
- loopx/capabilities/reward_memory/scoped_feedback.py +173 -0
- loopx/capabilities/semantic_preference/__init__.py +12 -0
- loopx/capabilities/semantic_preference/cli.py +189 -0
- loopx/capabilities/semantic_preference/contract.py +592 -0
- loopx/capabilities/semantic_preference/reward_memory.py +62 -0
- loopx/capabilities/value_connectors/__init__.py +1 -0
- loopx/capabilities/value_connectors/cli.py +401 -0
- loopx/capabilities/value_connectors/finance_extension_migration.py +108 -0
- loopx/capabilities/value_connectors/install_check.py +147 -0
- loopx/capabilities/value_connectors/planner.py +733 -0
- loopx/capabilities/value_connectors/source_map.py +446 -0
- loopx/claude_goal_baseline.py +138 -0
- loopx/claude_goal_mode/__init__.py +23 -0
- loopx/claude_goal_mode/hooks/goal_policy.py +212 -0
- loopx/claude_goal_mode/hooks/goal_state.py +139 -0
- loopx/claude_goal_mode/mcp/loopx_mcp.py +167 -0
- loopx/claude_goal_mode/scripts/connect.py +103 -0
- loopx/claude_goal_mode/scripts/goalmode_cmd.py +241 -0
- loopx/claude_goal_mode/scripts/install.py +328 -0
- loopx/claude_goal_mode/statusline/goal_status.py +97 -0
- loopx/cli.py +836 -0
- loopx/cli_commands/__init__.py +334 -0
- loopx/cli_commands/_host_thread.py +13 -0
- loopx/cli_commands/agentissue_runner_flow.py +447 -0
- loopx/cli_commands/agents_last_exam.py +160 -0
- loopx/cli_commands/agents_last_exam_baked_input.py +302 -0
- loopx/cli_commands/agents_last_exam_host_codex.py +374 -0
- loopx/cli_commands/agents_last_exam_launch_dry_run.py +372 -0
- loopx/cli_commands/agents_last_exam_local_plan.py +322 -0
- loopx/cli_commands/agents_last_exam_runner_source.py +352 -0
- loopx/cli_commands/agents_last_exam_task_material.py +335 -0
- loopx/cli_commands/agents_last_exam_validation_gate.py +236 -0
- loopx/cli_commands/benchmark_boundary.py +499 -0
- loopx/cli_commands/benchmark_dispatch.py +161 -0
- loopx/cli_commands/benchmark_release_outcome.py +123 -0
- loopx/cli_commands/benchmark_review_lifecycle.py +1275 -0
- loopx/cli_commands/benchmark_run_ledger.py +763 -0
- loopx/cli_commands/benchmark_run_ledger_case_analysis.py +249 -0
- loopx/cli_commands/benchmark_run_ledger_classification.py +45 -0
- loopx/cli_commands/benchmark_run_ledger_maintenance.py +486 -0
- loopx/cli_commands/benchmark_run_ledger_maintenance_registration.py +342 -0
- loopx/cli_commands/benchmark_run_ledger_maintenance_rendering.py +233 -0
- loopx/cli_commands/benchmark_run_ledger_parity.py +92 -0
- loopx/cli_commands/bootstrap_connect.py +238 -0
- loopx/cli_commands/canary.py +707 -0
- loopx/cli_commands/canary_release_qualification.py +79 -0
- loopx/cli_commands/capability.py +96 -0
- loopx/cli_commands/doctor.py +43 -0
- loopx/cli_commands/dreaming.py +143 -0
- loopx/cli_commands/edgebench.py +205 -0
- loopx/cli_commands/evidence_log.py +275 -0
- loopx/cli_commands/explore.py +989 -0
- loopx/cli_commands/explore_planning_commands.py +157 -0
- loopx/cli_commands/extension.py +271 -0
- loopx/cli_commands/first_run_report.py +73 -0
- loopx/cli_commands/goal_channel.py +656 -0
- loopx/cli_commands/handoff_mode.py +158 -0
- loopx/cli_commands/history.py +622 -0
- loopx/cli_commands/host_mode_plan.py +113 -0
- loopx/cli_commands/lark_inbox.py +431 -0
- loopx/cli_commands/lark_kanban.py +629 -0
- loopx/cli_commands/ml_experiment.py +321 -0
- loopx/cli_commands/multi_agent.py +211 -0
- loopx/cli_commands/opencode2_goal_worker.py +217 -0
- loopx/cli_commands/pr_review.py +167 -0
- loopx/cli_commands/presentation.py +218 -0
- loopx/cli_commands/preset.py +96 -0
- loopx/cli_commands/project.py +150 -0
- loopx/cli_commands/project_lifecycle.py +915 -0
- loopx/cli_commands/quota.py +859 -0
- loopx/cli_commands/quota_registration.py +241 -0
- loopx/cli_commands/quota_request.py +113 -0
- loopx/cli_commands/ready_score.py +110 -0
- loopx/cli_commands/registry_admin.py +975 -0
- loopx/cli_commands/registry_admin_configure.py +344 -0
- loopx/cli_commands/registry_admin_peer.py +84 -0
- loopx/cli_commands/registry_authority.py +218 -0
- loopx/cli_commands/review_batch.py +146 -0
- loopx/cli_commands/slash_commands.py +145 -0
- loopx/cli_commands/start_goal.py +251 -0
- loopx/cli_commands/starter.py +175 -0
- loopx/cli_commands/starter_bootstrap.py +179 -0
- loopx/cli_commands/starter_bootstrap_registration.py +198 -0
- loopx/cli_commands/starter_runtime_idle.py +107 -0
- loopx/cli_commands/starter_scheduler.py +207 -0
- loopx/cli_commands/starter_session_runtime.py +152 -0
- loopx/cli_commands/starter_visible_common.py +54 -0
- loopx/cli_commands/starter_visible_driver.py +161 -0
- loopx/cli_commands/starter_visible_pilot.py +278 -0
- loopx/cli_commands/status.py +867 -0
- loopx/cli_commands/status_registration.py +239 -0
- loopx/cli_commands/summary_all.py +222 -0
- loopx/cli_commands/support_control.py +809 -0
- loopx/cli_commands/support_control_registry.py +68 -0
- loopx/cli_commands/support_control_supervisor.py +289 -0
- loopx/cli_commands/task_lease.py +306 -0
- loopx/cli_commands/terminal_bench_adapter.py +717 -0
- loopx/cli_commands/terminal_bench_environment_result.py +1246 -0
- loopx/cli_commands/todo.py +940 -0
- loopx/cli_commands/todo_argument_validation.py +572 -0
- loopx/cli_commands/todo_event.py +114 -0
- loopx/cli_commands/turn.py +804 -0
- loopx/cli_commands/version.py +46 -0
- loopx/cli_commands/worker_bridge.py +659 -0
- loopx/cli_rollout.py +314 -0
- loopx/codex_cli_goal_tui.py +672 -0
- loopx/codex_cli_probe.py +1530 -0
- loopx/codex_cli_probe_markdown.py +935 -0
- loopx/codex_cli_runtime_probe.py +733 -0
- loopx/codex_cli_scheduler.py +564 -0
- loopx/codex_goal_baseline.py +620 -0
- loopx/configuration_catalog.py +617 -0
- loopx/configure_goal.py +1375 -0
- loopx/contract.py +996 -0
- loopx/control_plane/__init__.py +71 -0
- loopx/control_plane/agents/__init__.py +1 -0
- loopx/control_plane/agents/agent_lane_recommendation.py +516 -0
- loopx/control_plane/agents/agent_scope.py +1578 -0
- loopx/control_plane/agents/agent_scope_frontier.py +60 -0
- loopx/control_plane/agents/capability_gate.py +531 -0
- loopx/control_plane/agents/identity.py +140 -0
- loopx/control_plane/agents/legacy_migration.py +169 -0
- loopx/control_plane/agents/management_projection.py +658 -0
- loopx/control_plane/agents/material_frontier.py +608 -0
- loopx/control_plane/agents/material_handoff.py +156 -0
- loopx/control_plane/agents/multi_agent/__init__.py +1 -0
- loopx/control_plane/agents/multi_agent/codex_executable.py +207 -0
- loopx/control_plane/agents/multi_agent/collective_round_ledger.py +387 -0
- loopx/control_plane/agents/multi_agent/contract.py +474 -0
- loopx/control_plane/agents/multi_agent/recipe.py +110 -0
- loopx/control_plane/agents/multi_agent/role_successor.py +297 -0
- loopx/control_plane/agents/multi_agent/runtime_scripts.py +426 -0
- loopx/control_plane/agents/multi_agent/visible_launch_policy.py +149 -0
- loopx/control_plane/agents/multi_agent/visible_wake_scheduler.py +392 -0
- loopx/control_plane/agents/profile.py +216 -0
- loopx/control_plane/agents/runtime_model.py +73 -0
- loopx/control_plane/agents/subagent_activity.py +164 -0
- loopx/control_plane/agents/supervisor.py +544 -0
- loopx/control_plane/agents/supervisor_events.py +462 -0
- loopx/control_plane/agents/supervisor_inject.py +204 -0
- loopx/control_plane/agents/work_mode.py +56 -0
- loopx/control_plane/agents/workspace_guard.py +364 -0
- loopx/control_plane/effect_program.py +644 -0
- loopx/control_plane/goals/__init__.py +1 -0
- loopx/control_plane/goals/active_state_event_projection.py +103 -0
- loopx/control_plane/goals/active_state_metadata.py +47 -0
- loopx/control_plane/goals/active_state_sections.py +58 -0
- loopx/control_plane/goals/configure_goal_service.py +354 -0
- loopx/control_plane/goals/contract_health.py +132 -0
- loopx/control_plane/goals/dreaming.py +152 -0
- loopx/control_plane/goals/global_registry_health.py +199 -0
- loopx/control_plane/goals/global_registry_shadow.py +33 -0
- loopx/control_plane/goals/goal_channel.py +34 -0
- loopx/control_plane/goals/goal_channel_projection.py +560 -0
- loopx/control_plane/goals/goal_frontier/__init__.py +1917 -0
- loopx/control_plane/goals/goal_frontier/ack_policy.py +149 -0
- loopx/control_plane/goals/goal_frontier/outcome_continuity.py +437 -0
- loopx/control_plane/goals/goal_frontier/replan_rules.py +210 -0
- loopx/control_plane/goals/goal_frontier/semantic_history.py +314 -0
- loopx/control_plane/goals/goal_frontier/terminal.py +180 -0
- loopx/control_plane/goals/goal_vision.py +443 -0
- loopx/control_plane/goals/goal_vision_policy.py +36 -0
- loopx/control_plane/goals/goal_vision_state.py +62 -0
- loopx/control_plane/goals/goal_vision_wait.py +290 -0
- loopx/control_plane/goals/path_resolution.py +20 -0
- loopx/control_plane/goals/start_contract.py +206 -0
- loopx/control_plane/goals/vision_checkpoint.py +92 -0
- loopx/control_plane/handoff/__init__.py +1 -0
- loopx/control_plane/handoff/cross_runtime_impl_review.py +311 -0
- loopx/control_plane/handoff/delivery_contract.py +161 -0
- loopx/control_plane/handoff/handoff_runs.py +71 -0
- loopx/control_plane/handoff/project_handoff.py +155 -0
- loopx/control_plane/handoff/review_batch.py +463 -0
- loopx/control_plane/handoff/review_packet_context.py +216 -0
- loopx/control_plane/heartbeat/agent.py +173 -0
- loopx/control_plane/heartbeat/budget.py +66 -0
- loopx/control_plane/heartbeat/builder.py +501 -0
- loopx/control_plane/heartbeat/host.py +64 -0
- loopx/control_plane/heartbeat/rules.py +68 -0
- loopx/control_plane/heartbeat/task_body.py +759 -0
- loopx/control_plane/heartbeat/visible_goal.py +86 -0
- loopx/control_plane/projects/__init__.py +1 -0
- loopx/control_plane/projects/contract.py +25 -0
- loopx/control_plane/projects/registry.py +663 -0
- loopx/control_plane/quota/__init__.py +1 -0
- loopx/control_plane/quota/cli_projection.py +704 -0
- loopx/control_plane/quota/decision_summary.py +431 -0
- loopx/control_plane/quota/effect_program.py +152 -0
- loopx/control_plane/quota/error_codes.py +19 -0
- loopx/control_plane/quota/goal_boundary.py +464 -0
- loopx/control_plane/quota/heartbeat_receipt.py +277 -0
- loopx/control_plane/quota/heartbeat_recommendation.py +718 -0
- loopx/control_plane/quota/host_poll_receipts.py +162 -0
- loopx/control_plane/quota/live_decision.py +142 -0
- loopx/control_plane/quota/monitor_poll.py +786 -0
- loopx/control_plane/quota/policy_constants.py +40 -0
- loopx/control_plane/quota/projection_repair.py +262 -0
- loopx/control_plane/quota/recent_runs.py +210 -0
- loopx/control_plane/quota/scheduler_ack.py +490 -0
- loopx/control_plane/quota/selected_todo_projection.py +139 -0
- loopx/control_plane/quota/settlement.py +437 -0
- loopx/control_plane/quota/settlement_cli.py +246 -0
- loopx/control_plane/quota/settlement_validation.py +64 -0
- loopx/control_plane/quota/settlement_workspace_causality.py +180 -0
- loopx/control_plane/quota/should_run.py +249 -0
- loopx/control_plane/quota/should_run_packet.py +1165 -0
- loopx/control_plane/quota/should_run_prepare.py +675 -0
- loopx/control_plane/quota/slot_accounting.py +1123 -0
- loopx/control_plane/quota/spend_sources.py +11 -0
- loopx/control_plane/quota/stall_repair.py +397 -0
- loopx/control_plane/quota/states.py +29 -0
- loopx/control_plane/quota/task_orchestration.py +448 -0
- loopx/control_plane/quota/task_orchestration_admission.py +497 -0
- loopx/control_plane/quota/turn_envelope.py +889 -0
- loopx/control_plane/quota/usage_summary.py +140 -0
- loopx/control_plane/reward_memory.py +43 -0
- loopx/control_plane/runtime/__init__.py +2 -0
- loopx/control_plane/runtime/active_user_assisted_pilot.py +275 -0
- loopx/control_plane/runtime/agent_scoped_evidence_log.py +435 -0
- loopx/control_plane/runtime/decision_freshness.py +203 -0
- loopx/control_plane/runtime/event_ledger.py +197 -0
- loopx/control_plane/runtime/event_store_migration_bridge.py +196 -0
- loopx/control_plane/runtime/goal_project_route.py +70 -0
- loopx/control_plane/runtime/local_state_write_correctness.py +242 -0
- loopx/control_plane/runtime/promotion_readiness.py +152 -0
- loopx/control_plane/runtime/public_safety.py +120 -0
- loopx/control_plane/runtime/run_artifacts.py +78 -0
- loopx/control_plane/runtime/run_compaction.py +397 -0
- loopx/control_plane/runtime/run_context_retention.py +241 -0
- loopx/control_plane/runtime/run_history.py +132 -0
- loopx/control_plane/runtime/run_index_duplicates.py +205 -0
- loopx/control_plane/runtime/run_index_rebuild.py +263 -0
- loopx/control_plane/runtime/run_ingest_health.py +336 -0
- loopx/control_plane/runtime/runtime_projection_route.py +624 -0
- loopx/control_plane/runtime/runtime_projection_writer.py +98 -0
- loopx/control_plane/runtime/session_runtime.py +339 -0
- loopx/control_plane/runtime/shared_runtime_material_projection.py +332 -0
- loopx/control_plane/runtime/shared_runtime_refresh_projection.py +183 -0
- loopx/control_plane/runtime/stale_latest_run.py +90 -0
- loopx/control_plane/runtime/status_classifications.py +49 -0
- loopx/control_plane/runtime/status_projection_cache.py +235 -0
- loopx/control_plane/runtime/stride_observation.py +144 -0
- loopx/control_plane/runtime/time.py +39 -0
- loopx/control_plane/runtime/trajectory_hygiene.py +149 -0
- loopx/control_plane/runtime/validation_command.py +69 -0
- loopx/control_plane/scheduler/__init__.py +1 -0
- loopx/control_plane/scheduler/ack.py +329 -0
- loopx/control_plane/scheduler/arbitration.py +188 -0
- loopx/control_plane/scheduler/automation_liveness.py +183 -0
- loopx/control_plane/scheduler/execution_context.py +555 -0
- loopx/control_plane/scheduler/external_evidence_observation.py +428 -0
- loopx/control_plane/scheduler/monitor_display.py +143 -0
- loopx/control_plane/scheduler/monitor_poll_policy.py +161 -0
- loopx/control_plane/scheduler/monitor_poll_writeback.py +351 -0
- loopx/control_plane/scheduler/monitor_target.py +64 -0
- loopx/control_plane/scheduler/monitor_todo.py +146 -0
- loopx/control_plane/scheduler/monitor_wait.py +237 -0
- loopx/control_plane/scheduler/scheduler_hint.py +1284 -0
- loopx/control_plane/scheduler/state.py +354 -0
- loopx/control_plane/scheduler/state_transition_rules.py +179 -0
- loopx/control_plane/scheduler/time.py +10 -0
- loopx/control_plane/settlement_driver.py +293 -0
- loopx/control_plane/status/__init__.py +6 -0
- loopx/control_plane/status/active_state_projection.py +105 -0
- loopx/control_plane/status/agent_lane_projection.py +375 -0
- loopx/control_plane/status/attention_projection.py +74 -0
- loopx/control_plane/status/autonomous_replan_projection.py +103 -0
- loopx/control_plane/status/collection.py +140 -0
- loopx/control_plane/status/contract_projection.py +31 -0
- loopx/control_plane/status/dreaming_projection.py +52 -0
- loopx/control_plane/status/goal_attention_projection.py +157 -0
- loopx/control_plane/status/lifecycle_projection.py +110 -0
- loopx/control_plane/status/monitor_display_projection.py +69 -0
- loopx/control_plane/status/registry_health_projection.py +75 -0
- loopx/control_plane/status/run_projection.py +70 -0
- loopx/control_plane/status/runtime_summaries.py +161 -0
- loopx/control_plane/testing/__init__.py +1 -0
- loopx/control_plane/testing/actual_default_model_behavior_portfolio.py +1371 -0
- loopx/control_plane/testing/canary_harness.py +182 -0
- loopx/control_plane/testing/capability_monitor_repair_tool_behavior.py +674 -0
- loopx/control_plane/testing/cli_output_budget.py +807 -0
- loopx/control_plane/testing/cli_output_differential.py +250 -0
- loopx/control_plane/testing/cli_output_semantics.py +87 -0
- loopx/control_plane/testing/control_plane_composition_scenarios.py +225 -0
- loopx/control_plane/testing/decision_replay.py +268 -0
- loopx/control_plane/testing/doubao_model_behavior_actor.py +559 -0
- loopx/control_plane/testing/model_behavior_corpus.py +344 -0
- loopx/control_plane/testing/model_behavior_qualification.py +769 -0
- loopx/control_plane/testing/model_behavior_retained_cases.py +235 -0
- loopx/control_plane/testing/model_tool_behavior.py +536 -0
- loopx/control_plane/testing/onboarding_model_behavior_qualification.py +642 -0
- loopx/control_plane/testing/quota_fixtures.py +208 -0
- loopx/control_plane/testing/quota_should_run_parity.py +57 -0
- loopx/control_plane/testing/release_commit_qualification.py +671 -0
- loopx/control_plane/testing/replan_semantic_action_behavior.py +1302 -0
- loopx/control_plane/testing/scoped_gate_successor_tool_behavior.py +527 -0
- loopx/control_plane/testing/selected_todo_tool_behavior.py +1002 -0
- loopx/control_plane/testing/terminal_settlement_tool_behavior.py +656 -0
- loopx/control_plane/todos/__init__.py +1 -0
- loopx/control_plane/todos/active_state_editing.py +296 -0
- loopx/control_plane/todos/active_state_todo_parser.py +138 -0
- loopx/control_plane/todos/active_state_todos.py +175 -0
- loopx/control_plane/todos/addition.py +103 -0
- loopx/control_plane/todos/claim_visibility.py +253 -0
- loopx/control_plane/todos/completed_archive.py +139 -0
- loopx/control_plane/todos/completion_fence.py +49 -0
- loopx/control_plane/todos/completion_policy.py +153 -0
- loopx/control_plane/todos/completion_validation.py +248 -0
- loopx/control_plane/todos/completion_validation_accountability.py +27 -0
- loopx/control_plane/todos/completion_validation_projection.py +57 -0
- loopx/control_plane/todos/contract.py +1476 -0
- loopx/control_plane/todos/decision_scope.py +554 -0
- loopx/control_plane/todos/deferred_resume.py +546 -0
- loopx/control_plane/todos/durable_completion.py +201 -0
- loopx/control_plane/todos/event_writeback.py +484 -0
- loopx/control_plane/todos/frontier_deadline.py +132 -0
- loopx/control_plane/todos/handoff_gate.py +283 -0
- loopx/control_plane/todos/handoff_mode.py +444 -0
- loopx/control_plane/todos/handoff_note.py +202 -0
- loopx/control_plane/todos/line_update.py +361 -0
- loopx/control_plane/todos/list_projection.py +205 -0
- loopx/control_plane/todos/markdown.py +199 -0
- loopx/control_plane/todos/monitor_metadata.py +88 -0
- loopx/control_plane/todos/mutation_authority.py +299 -0
- loopx/control_plane/todos/projection.py +655 -0
- loopx/control_plane/todos/quota_summary.py +1138 -0
- loopx/control_plane/todos/route_continuation.py +267 -0
- loopx/control_plane/todos/succession_warning.py +174 -0
- loopx/control_plane/todos/summary_item.py +223 -0
- loopx/control_plane/todos/text.py +30 -0
- loopx/control_plane/todos/todo_index.py +226 -0
- loopx/control_plane/todos/todo_summary.py +1458 -0
- loopx/control_plane/todos/unblock_resume.py +326 -0
- loopx/control_plane/todos/user_gate.py +263 -0
- loopx/control_plane/todos/write_hint.py +63 -0
- loopx/control_plane/todos/write_policy.py +135 -0
- loopx/control_plane/turn_driver/__init__.py +85 -0
- loopx/control_plane/turn_driver/codex_cli.py +502 -0
- loopx/control_plane/turn_driver/driver.py +355 -0
- loopx/control_plane/turn_driver/executor.py +1468 -0
- loopx/control_plane/turn_driver/loop_controller.py +669 -0
- loopx/control_plane/turn_driver/settlement.py +318 -0
- loopx/control_plane/turn_driver/transaction.py +375 -0
- loopx/control_plane/work_items/__init__.py +1 -0
- loopx/control_plane/work_items/attention_fields.py +56 -0
- loopx/control_plane/work_items/attention_item.py +77 -0
- loopx/control_plane/work_items/attention_queue.py +322 -0
- loopx/control_plane/work_items/attention_routing.py +213 -0
- loopx/control_plane/work_items/autonomous_candidates.py +135 -0
- loopx/control_plane/work_items/autonomous_replan_ack.py +276 -0
- loopx/control_plane/work_items/autonomous_replan_obligation.py +786 -0
- loopx/control_plane/work_items/backlog_hygiene.py +59 -0
- loopx/control_plane/work_items/capability_monitor_fallback.py +221 -0
- loopx/control_plane/work_items/delivery_batch_scale.py +66 -0
- loopx/control_plane/work_items/delivery_outcome.py +152 -0
- loopx/control_plane/work_items/delivery_signals.py +113 -0
- loopx/control_plane/work_items/execution_obligation.py +235 -0
- loopx/control_plane/work_items/goal_route_hint.py +320 -0
- loopx/control_plane/work_items/interaction_contract.py +1540 -0
- loopx/control_plane/work_items/issue_meta_surface.py +159 -0
- loopx/control_plane/work_items/lifecycle.py +139 -0
- loopx/control_plane/work_items/operator_inbox.py +266 -0
- loopx/control_plane/work_items/outcome_followthrough.py +69 -0
- loopx/control_plane/work_items/primary_action.py +326 -0
- loopx/control_plane/work_items/progress_observation.py +630 -0
- loopx/control_plane/work_items/project_asset.py +675 -0
- loopx/control_plane/work_items/repair_delta.py +693 -0
- loopx/control_plane/work_items/runtime_capability_reentry.py +168 -0
- loopx/control_plane/work_items/semantic_replan_writeback.py +177 -0
- loopx/control_plane/work_items/status_contract.py +49 -0
- loopx/control_plane/work_items/task_graph.py +1046 -0
- loopx/control_plane/work_items/task_lease.py +1254 -0
- loopx/control_plane/work_items/task_lease_settlement.py +422 -0
- loopx/control_plane/work_items/work_lane.py +510 -0
- loopx/control_plane/work_items/work_lane_context.py +161 -0
- loopx/demo.py +247 -0
- loopx/diagnose.py +633 -0
- loopx/doctor.py +1251 -0
- loopx/domain_packs/__init__.py +1 -0
- loopx/domain_packs/issue_fix.py +571 -0
- loopx/domain_packs/ml_experiment.py +854 -0
- loopx/domain_state.py +137 -0
- loopx/dreaming.py +706 -0
- loopx/entrypoint.py +16 -0
- loopx/event_sourced_state.py +981 -0
- loopx/execution_profile.py +286 -0
- loopx/experiments/__init__.py +1 -0
- loopx/experiments/planner_worker/__init__.py +1 -0
- loopx/experiments/planner_worker/contract.py +523 -0
- loopx/experiments/planner_worker/runtime.py +391 -0
- loopx/experiments/planner_worker/traex.py +461 -0
- loopx/explore_graph.py +11 -0
- loopx/extensions/__init__.py +1 -0
- loopx/extensions/bundled.py +28 -0
- loopx/extensions/execution_envelope.py +126 -0
- loopx/extensions/lark/__init__.py +11 -0
- loopx/extensions/lark/event_collector.py +478 -0
- loopx/extensions/lark/event_collector_runtime.py +506 -0
- loopx/extensions/lark/event_inbox.py +454 -0
- loopx/extensions/lark/extension.toml +88 -0
- loopx/extensions/lark/goal_channel.py +44 -0
- loopx/extensions/lark/goal_channel_contracts.py +388 -0
- loopx/extensions/lark/goal_channel_lifecycle.py +218 -0
- loopx/extensions/lark/goal_channel_runtime.py +792 -0
- loopx/extensions/lark/goal_channel_setup.py +805 -0
- loopx/extensions/lark/goal_channel_targets.py +215 -0
- loopx/extensions/lark/goal_channel_transport.py +281 -0
- loopx/extensions/lark/inbox_reactions.py +650 -0
- loopx/extensions/lark/inbox_reply.py +430 -0
- loopx/extensions/lark/presentation/__init__.py +11 -0
- loopx/extensions/lark/presentation/explore_results.py +2276 -0
- loopx/extensions/lark/presentation/explore_singleflight.py +127 -0
- loopx/extensions/lark/presentation/explore_source_guard.py +121 -0
- loopx/extensions/lark/presentation/explore_stage_document.py +703 -0
- loopx/extensions/lark/presentation/explore_visual_integrity.py +122 -0
- loopx/extensions/lark/presentation/explore_visual_readback.py +452 -0
- loopx/extensions/lark/presentation/explore_visual_styles.py +156 -0
- loopx/extensions/lark/presentation/issue_fix_surface.py +612 -0
- loopx/extensions/lark/presentation/kanban.py +2791 -0
- loopx/extensions/lark/presentation/message_card.py +112 -0
- loopx/extensions/lark/presentation/periodic_report.py +261 -0
- loopx/extensions/lark/presentation/projection_rows.py +600 -0
- loopx/extensions/lark/presentation/record_io.py +95 -0
- loopx/extensions/lark/presentation/sync_receipt.py +145 -0
- loopx/extensions/lark/private_json.py +40 -0
- loopx/extensions/lark/provider.py +86 -0
- loopx/extensions/lark/reviewer_notification.py +604 -0
- loopx/extensions/manifest.py +385 -0
- loopx/extensions/openviking_periodic_report/__init__.py +17 -0
- loopx/extensions/openviking_periodic_report/activation.py +173 -0
- loopx/extensions/openviking_periodic_report/extension.toml +17 -0
- loopx/extensions/openviking_periodic_report/provider.py +355 -0
- loopx/extensions/openviking_periodic_report/sink.py +117 -0
- loopx/extensions/openviking_semantic_preference/__init__.py +5 -0
- loopx/extensions/openviking_semantic_preference/extension.toml +16 -0
- loopx/extensions/openviking_semantic_preference/history_export.py +484 -0
- loopx/extensions/openviking_semantic_preference/project_peer.py +68 -0
- loopx/extensions/openviking_semantic_preference/provider.py +312 -0
- loopx/extensions/presentation.py +979 -0
- loopx/extensions/process_runtime.py +204 -0
- loopx/extensions/readiness.py +168 -0
- loopx/extensions/runtime.py +931 -0
- loopx/extensions/scaffold.py +335 -0
- loopx/feedback.py +581 -0
- loopx/file_lock.py +382 -0
- loopx/global_registry.py +842 -0
- loopx/global_risks.py +970 -0
- loopx/global_todos.py +568 -0
- loopx/handoff_budget.py +28 -0
- loopx/heartbeat_prequota.py +80 -0
- loopx/heartbeat_prompt.py +159 -0
- loopx/help_surface.py +516 -0
- loopx/history.py +1507 -0
- loopx/host_loop_activation.py +1311 -0
- loopx/host_mode_planner.py +991 -0
- loopx/install_contract.py +1 -0
- loopx/interface_budget.py +196 -0
- loopx/long_task_cadence.py +208 -0
- loopx/materials.py +185 -0
- loopx/ml_experiment.py +3 -0
- loopx/onboarding.py +214 -0
- loopx/opencode2_goal_mode/README.md +81 -0
- loopx/opencode2_goal_mode/__init__.py +9 -0
- loopx/opencode2_goal_mode/opencode2-goal-worker.mjs +1018 -0
- loopx/opencode_goal_mode/README.md +99 -0
- loopx/opencode_goal_mode/__init__.py +13 -0
- loopx/opencode_goal_mode/goal-bridge-runtime.mjs +858 -0
- loopx/opencode_goal_mode/loopx-goal.js +8 -0
- loopx/operator_gate.py +420 -0
- loopx/orchestration.py +127 -0
- loopx/paths.py +59 -0
- loopx/pi_goal_mode/README.md +67 -0
- loopx/pi_goal_mode/__init__.py +13 -0
- loopx/pi_goal_mode/loopx-goal.ts +254 -0
- loopx/pi_goal_mode/pi-goal-loop-runtime.mjs +574 -0
- loopx/pr_review.py +1206 -0
- loopx/presentation/__init__.py +1 -0
- loopx/presentation/explore_views.py +1334 -0
- loopx/presentation/markdown.py +61 -0
- loopx/presentation/projection_source_reconcile.py +140 -0
- loopx/presentation/public_safety.py +42 -0
- loopx/presentation/renderers/__init__.py +17 -0
- loopx/presentation/renderers/goal_channel_html.py +269 -0
- loopx/presentation/renderers/periodic_report_html.py +786 -0
- loopx/presentation/renderers/periodic_report_markdown.py +184 -0
- loopx/presentation/renderers/quota_event_markdown.py +116 -0
- loopx/presentation/renderers/quota_markdown.py +1112 -0
- loopx/presentation/renderers/status_markdown.py +1570 -0
- loopx/presentation/renderers/trajectory_hygiene_markdown.py +39 -0
- loopx/presentation/renderers/turn_envelope_markdown.py +33 -0
- loopx/presentation/sinks/__init__.py +5 -0
- loopx/presentation/sinks/openviking_periodic_report.py +7 -0
- loopx/presentation/static_site.py +691 -0
- loopx/presets.py +369 -0
- loopx/project_alias.py +217 -0
- loopx/project_map.py +589 -0
- loopx/project_prompt.py +1153 -0
- loopx/project_skill_cli.py +125 -0
- loopx/project_skill_delivery.py +470 -0
- loopx/project_uninstall.py +462 -0
- loopx/promotion_gate.py +197 -0
- loopx/quota.py +1197 -0
- loopx/ready_score.py +413 -0
- loopx/registry.py +621 -0
- loopx/registry_writability.py +64 -0
- loopx/release_candidate.py +148 -0
- loopx/release_manifest.py +316 -0
- loopx/repository_identity.py +100 -0
- loopx/review_packet.py +1024 -0
- loopx/rollout_event_log.py +505 -0
- loopx/runtime.py +112 -0
- loopx/self_update.py +750 -0
- loopx/session_runtime.py +418 -0
- loopx/skill_install_readback.py +500 -0
- loopx/slash_command_install.py +1393 -0
- loopx/slash_commands.py +264 -0
- loopx/state_backup.py +573 -0
- loopx/state_migration.py +350 -0
- loopx/state_projection.py +809 -0
- loopx/state_refresh.py +1416 -0
- loopx/status.py +1383 -0
- loopx/status_server.py +935 -0
- loopx/summary_all.py +725 -0
- loopx/terminal_bench_agent.py +2056 -0
- loopx/thread_agent_binding.py +408 -0
- loopx/todo_followups.py +168 -0
- loopx/todo_suggestion_prompt.py +204 -0
- loopx/todos.py +2229 -0
- loopx/turn_identity.py +17 -0
- loopx/upgrade.py +1083 -0
- loopx/visible_governance.py +667 -0
- loopx/visible_multi_agent_launcher.py +1253 -0
- loopx/visible_multi_agent_tmux.py +429 -0
- loopx/worker_bridge.py +1574 -0
- loopx-0.4.8.dist-info/METADATA +708 -0
- loopx-0.4.8.dist-info/RECORD +811 -0
- loopx-0.4.8.dist-info/WHEEL +5 -0
- loopx-0.4.8.dist-info/entry_points.txt +5 -0
- loopx-0.4.8.dist-info/licenses/LICENSE +202 -0
- loopx-0.4.8.dist-info/licenses/LICENSE-MIT +21 -0
- loopx-0.4.8.dist-info/licenses/NOTICE +6 -0
- loopx-0.4.8.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Smart entry for the `/loopx` slash command — the loopx setup helper.
|
|
3
|
+
|
|
4
|
+
LoopX does NOT own the run loop on Claude Code. Claude Code's native `/loop` is
|
|
5
|
+
the scheduler/executor; loopx provides the control-plane protocol (the MCP tools
|
|
6
|
+
+ a per-iteration `.claude/loop.md`). `/loopx` just sets a goal up and hands off
|
|
7
|
+
to `/loop`.
|
|
8
|
+
|
|
9
|
+
Routing by first token:
|
|
10
|
+
<free text task> -> ONE-SHOT: ensure a goal exists for this project (bootstrap
|
|
11
|
+
if needed), register a default agent, add the task as a
|
|
12
|
+
todo, write `.claude/loop.md` (the protocol), and do one
|
|
13
|
+
bounded first segment. Then drive it with native `/loop`.
|
|
14
|
+
(no args) / on -> (re)write `.claude/loop.md` for this project's existing
|
|
15
|
+
goal and show how to drive it with `/loop`.
|
|
16
|
+
status -> the project's goal detail (objective, state, next, todos).
|
|
17
|
+
off -> remove `.claude/loop.md` (native `/loop` then has nothing
|
|
18
|
+
to run). Registry/goal are left intact.
|
|
19
|
+
|
|
20
|
+
The LoopX registry (`.loopx/registry.json`) is the single source of truth for
|
|
21
|
+
goal_id/agent/scope. The default agent `cc` is registered so the loopx identity
|
|
22
|
+
contract is active and `quota should-run --agent-id cc` is satisfied.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import re
|
|
28
|
+
import shutil
|
|
29
|
+
import subprocess
|
|
30
|
+
import sys
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
HERE = Path(__file__).resolve().parent
|
|
34
|
+
DEFAULT_AGENT = "cc"
|
|
35
|
+
|
|
36
|
+
# registry-driven context, shared with the hooks/MCP
|
|
37
|
+
sys.path.insert(0, str(HERE.parent / "hooks"))
|
|
38
|
+
from goal_state import goal_context, find_registry, loop_md_path # noqa: E402
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def gh_prefix():
|
|
42
|
+
"""Cross-platform loopx invocation: the CLI shim if on PATH, else the module."""
|
|
43
|
+
exe = shutil.which("loopx")
|
|
44
|
+
return [exe] if exe else [sys.executable, "-m", "loopx.cli"]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def gh(args, cwd=None):
|
|
48
|
+
return subprocess.run(gh_prefix() + args, cwd=cwd, capture_output=True, text=True, timeout=120)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def slug(name: str) -> str:
|
|
52
|
+
s = re.sub(r"[^a-zA-Z0-9]+", "-", name).strip("-").lower() or "project"
|
|
53
|
+
return f"cc-{s}"[:48]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def loop_md_content(goal_id, agent_id) -> str:
|
|
57
|
+
"""The per-iteration protocol that native `/loop` runs (written to
|
|
58
|
+
.claude/loop.md). loopx's should_run is the deterministic per-tick gate; the
|
|
59
|
+
agent uses the wired loopx MCP tools, never raw CLI guessing.
|
|
60
|
+
|
|
61
|
+
The leading `loopx:armed` marker persists WHICH goal /loopx armed for this
|
|
62
|
+
project, so goal_state.goal_context() resolves the right goal in multi-goal
|
|
63
|
+
registries instead of guessing from registry order."""
|
|
64
|
+
armed = json.dumps({"goal_id": goal_id, "agent_id": agent_id})
|
|
65
|
+
return (
|
|
66
|
+
f"<!-- loopx:armed {armed} -->\n"
|
|
67
|
+
f"loopx tick — advance goal `{goal_id}` (agent `{agent_id}`). Use the wired loopx MCP\n"
|
|
68
|
+
f"tools; do NOT run `loopx --help` or guess ids.\n\n"
|
|
69
|
+
f"1. Call `should_run()`. If should_run=false, say why in ONE line and STOP this\n"
|
|
70
|
+
f" iteration (do nothing else) — loopx has paused, gated, or converged.\n"
|
|
71
|
+
f"2. If should_run=true: `claim_task` the next open todo, do ONE bounded segment,\n"
|
|
72
|
+
f" then VERIFY it with a real check (build/test) — never claim success from\n"
|
|
73
|
+
f" reasoning — and `complete_task(..., agent_id=\"{agent_id}\", evidence=\"<ran + result>\")`.\n"
|
|
74
|
+
f"3. Stay within the goal's scope; do not start initiatives outside the todos.\n"
|
|
75
|
+
f" Irreversible actions (push/delete) only to finish work already authorized.\n"
|
|
76
|
+
f"4. Re-check `should_run()`; stop when should_run=false or no open todos remain.\n"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def write_loop_md(proj: Path, goal_id, agent_id) -> Path:
|
|
81
|
+
"""Write the protocol to <project>/.claude/loop.md (bare `/loop` runs it)."""
|
|
82
|
+
path = loop_md_path(proj)
|
|
83
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
84
|
+
path.write_text(loop_md_content(goal_id, agent_id), encoding="utf-8")
|
|
85
|
+
return path
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def goal_detail(ctx):
|
|
89
|
+
"""Objective (from the registry goal entry's active-state file) + live state
|
|
90
|
+
(from quota should-run) for the `/loopx status` detail view."""
|
|
91
|
+
gid, reg, agent = ctx.get("goal_id"), ctx.get("registry"), ctx.get("agent_id")
|
|
92
|
+
objective = ""
|
|
93
|
+
if reg:
|
|
94
|
+
try:
|
|
95
|
+
regp = Path(reg)
|
|
96
|
+
data = json.loads(regp.read_text(encoding="utf-8"))
|
|
97
|
+
entry = next((g for g in data.get("goals", []) if g.get("id") == gid), None)
|
|
98
|
+
sf = (entry or {}).get("state_file")
|
|
99
|
+
if sf:
|
|
100
|
+
text = (regp.parent.parent / sf).read_text(encoding="utf-8")
|
|
101
|
+
m = re.search(r"^objective:\s*(.+)$", text, re.MULTILINE)
|
|
102
|
+
if m:
|
|
103
|
+
objective = m.group(1).strip().strip('"').strip("'")
|
|
104
|
+
except Exception:
|
|
105
|
+
pass
|
|
106
|
+
payload = {}
|
|
107
|
+
try:
|
|
108
|
+
cmd = gh_prefix() + (["--registry", reg] if reg else []) + \
|
|
109
|
+
["--format", "json", "quota", "should-run", "--goal-id", gid]
|
|
110
|
+
if agent:
|
|
111
|
+
cmd += ["--agent-id", agent]
|
|
112
|
+
out = subprocess.run(cmd, capture_output=True, text=True, timeout=15)
|
|
113
|
+
payload = json.loads(out.stdout or "{}")
|
|
114
|
+
except Exception:
|
|
115
|
+
pass
|
|
116
|
+
return objective, payload
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def print_status(ctx):
|
|
120
|
+
gid = ctx.get("goal_id")
|
|
121
|
+
objective, d = goal_detail(ctx)
|
|
122
|
+
a = d.get("agent_todo_summary") or {}
|
|
123
|
+
done, open_ = a.get("done_count"), a.get("open_count")
|
|
124
|
+
gate = d.get("gate_prompt")
|
|
125
|
+
if gate:
|
|
126
|
+
state = f"⚠ needs you: {gate}"
|
|
127
|
+
elif d.get("should_run") is True:
|
|
128
|
+
state = "▶ running"
|
|
129
|
+
else:
|
|
130
|
+
state = f"⏸ {d.get('state') or 'paused'}" + (f" — {d['reason']}" if d.get("reason") else "")
|
|
131
|
+
print(f"goal : {gid}")
|
|
132
|
+
print(f"agent : {ctx.get('agent_id') or DEFAULT_AGENT}")
|
|
133
|
+
if objective:
|
|
134
|
+
print(f"objective : {objective}")
|
|
135
|
+
print(f"state : {state}")
|
|
136
|
+
if d.get("recommended_action"):
|
|
137
|
+
print(f"next : {d['recommended_action']}")
|
|
138
|
+
if done is not None or open_ is not None:
|
|
139
|
+
print(f"todos : {done} done / {open_} open")
|
|
140
|
+
for it in (a.get("first_open_items") or [])[:8]:
|
|
141
|
+
t = (it.get("text") or "").strip()
|
|
142
|
+
if t:
|
|
143
|
+
print(f" ▸ {t}")
|
|
144
|
+
armed = "armed (.claude/loop.md present)" if loop_md_path(Path.cwd()).exists() else "not armed (run /loopx to arm)"
|
|
145
|
+
print(f"loop : {armed} — drive with native /loop")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def main():
|
|
149
|
+
args = sys.argv[1:]
|
|
150
|
+
first = args[0] if args else None
|
|
151
|
+
proj = Path.cwd()
|
|
152
|
+
|
|
153
|
+
# off: remove the loop protocol; native /loop then has nothing to run.
|
|
154
|
+
if first == "off":
|
|
155
|
+
lp = loop_md_path(proj)
|
|
156
|
+
if not lp.exists():
|
|
157
|
+
print("goal-mode OFF (no .claude/loop.md here).")
|
|
158
|
+
return
|
|
159
|
+
try:
|
|
160
|
+
lp.unlink()
|
|
161
|
+
print("goal-mode OFF — removed .claude/loop.md. (Press Esc to stop a running /loop.)")
|
|
162
|
+
except OSError as e:
|
|
163
|
+
print(f"goal-mode OFF — could not remove {lp} ({e}); remove it manually.")
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
# status: full goal detail.
|
|
167
|
+
if first == "status":
|
|
168
|
+
ctx = goal_context(proj)
|
|
169
|
+
if not ctx:
|
|
170
|
+
print("loopx goal-mode: no goal in this project yet — run `/loopx <task>` to create one.")
|
|
171
|
+
return
|
|
172
|
+
print_status(ctx)
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
# bare (=on) / on: (re)arm THIS project's existing goal and show how to drive it.
|
|
176
|
+
if not args or first == "on":
|
|
177
|
+
ctx = goal_context(proj)
|
|
178
|
+
if not ctx or not ctx.get("goal_id"):
|
|
179
|
+
print("loopx goal-mode: ready — no goal set in this project yet.")
|
|
180
|
+
print("Tell me what to work on and I'll set it up:")
|
|
181
|
+
print(" /loopx <your goal> e.g. /loopx 写一个 RTL 模块并跑通仿真")
|
|
182
|
+
return
|
|
183
|
+
write_loop_md(proj, ctx["goal_id"], ctx.get("agent_id") or DEFAULT_AGENT)
|
|
184
|
+
print(f"goal-mode armed goal={ctx['goal_id']} agent={ctx.get('agent_id') or DEFAULT_AGENT}")
|
|
185
|
+
print("Drive it with native /loop: `/loop` (Claude self-paces) | `/loop 10m` (fixed).")
|
|
186
|
+
print("Stop with Esc; `/loopx off` removes the loop protocol.")
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
# free-text task -> one-shot setup + write loop.md + first bounded segment.
|
|
190
|
+
task = " ".join(args).strip().strip('"').strip("'")
|
|
191
|
+
reg = find_registry(proj)
|
|
192
|
+
if reg is not None:
|
|
193
|
+
ctx = goal_context(proj) or {}
|
|
194
|
+
goal_id = ctx.get("goal_id")
|
|
195
|
+
registry = str(reg)
|
|
196
|
+
else:
|
|
197
|
+
goal_id = slug(proj.name)
|
|
198
|
+
registry = str(proj / ".loopx" / "registry.json")
|
|
199
|
+
# Claude projects keep goal state under .claude/ (not the Codex-default .codex/)
|
|
200
|
+
state_file = f".claude/goals/{goal_id}/ACTIVE_GOAL_STATE.md"
|
|
201
|
+
r = gh(["bootstrap", "--project", str(proj), "--goal-id", goal_id,
|
|
202
|
+
"--objective", task, "--state-file", state_file, "--no-onboarding-scan"])
|
|
203
|
+
if "ok: `True`" not in r.stdout and "ok=True" not in r.stdout and r.returncode != 0:
|
|
204
|
+
print("[loopx] bootstrap failed:\n" + (r.stdout + r.stderr)[:600])
|
|
205
|
+
sys.exit(1)
|
|
206
|
+
|
|
207
|
+
if not goal_id:
|
|
208
|
+
print("[loopx] could not determine goal id")
|
|
209
|
+
sys.exit(1)
|
|
210
|
+
|
|
211
|
+
# register the default agent (identity contract) and add the task as a todo
|
|
212
|
+
gh(["--registry", registry, "configure-goal", "--goal-id", goal_id,
|
|
213
|
+
"--agent-model", "peer_v1", "--registered-agent", DEFAULT_AGENT, "--execute"])
|
|
214
|
+
add = gh(["--registry", registry, "--format", "json", "todo", "add",
|
|
215
|
+
"--goal-id", goal_id, "--role", "agent", "--text", task])
|
|
216
|
+
todo_id = ""
|
|
217
|
+
try:
|
|
218
|
+
todo_id = json.loads(add.stdout).get("todo_id", "")
|
|
219
|
+
except Exception:
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
# write the per-iteration protocol; the RUN happens when the user types /loop
|
|
223
|
+
# (this command only SETS UP the goal — it must not do the work itself).
|
|
224
|
+
write_loop_md(proj, goal_id, DEFAULT_AGENT)
|
|
225
|
+
|
|
226
|
+
tid = todo_id or "(see should_run output)"
|
|
227
|
+
print("goal set up. loopx is the control plane; Claude Code's native /loop is the runtime.")
|
|
228
|
+
print(f" goal_id : {goal_id}")
|
|
229
|
+
print(f" agent : {DEFAULT_AGENT}")
|
|
230
|
+
print(f" todo_id : {tid}")
|
|
231
|
+
print(f" scope : {proj}")
|
|
232
|
+
print(f" task : {task}")
|
|
233
|
+
print(f" wrote : .claude/loop.md (the per-tick protocol)")
|
|
234
|
+
print()
|
|
235
|
+
print("START WORKING — run native `/loop` (Claude self-paces) or `/loop 10m` (fixed cadence).")
|
|
236
|
+
print("Each /loop tick runs: should_run -> claim_task -> ONE bounded verified segment -> complete_task.")
|
|
237
|
+
print("Stop with Esc or `/loopx off`.")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
if __name__ == "__main__":
|
|
241
|
+
main()
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Install the loopx Claude Code adapter — EXPLICIT, scoped, opt-in.
|
|
3
|
+
|
|
4
|
+
This is NOT run by the default loopx installer. It is invoked deliberately
|
|
5
|
+
(`LOOPX_INSTALL_CLAUDE=1 scripts/install-local.sh`, or directly). It installs the
|
|
6
|
+
minimum: the loopx **MCP server** (the control-plane tools) + the **`/loopx`**
|
|
7
|
+
slash command. The run loop is Claude Code's native `/loop`; loopx only provides
|
|
8
|
+
the `should_run` protocol, so NO global hooks are wired by default.
|
|
9
|
+
|
|
10
|
+
python install.py --scope project [--project DIR] # this project only (preferred)
|
|
11
|
+
python install.py --scope user # all your Claude Code projects
|
|
12
|
+
python install.py --scope ... --harden # ALSO add the optional
|
|
13
|
+
# PreToolUse should_run gate
|
|
14
|
+
# + statusline at that scope
|
|
15
|
+
|
|
16
|
+
`--scope` is required (a deliberate choice). User scope is announced loudly. We
|
|
17
|
+
NEVER delete existing permission rules; legacy loopx entries get a printed
|
|
18
|
+
cleanup suggestion instead.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import shutil
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
PLUGIN_ROOT = Path(__file__).resolve().parents[1]
|
|
30
|
+
MCP_VENV = Path.home() / ".local" / "share" / "loopx" / "mcp-venv"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _p(*parts) -> str:
|
|
34
|
+
return str(PLUGIN_ROOT.joinpath(*parts)).replace("\\", "/")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _python_cmd() -> str:
|
|
38
|
+
"""Interpreter to bake into hook / statusline / command strings — robust where
|
|
39
|
+
only `python` (not `python3`) is registered."""
|
|
40
|
+
return shutil.which("python3") or shutil.which("python") or sys.executable
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def deep_merge(base: dict, add: dict) -> dict:
|
|
44
|
+
for k, v in add.items():
|
|
45
|
+
if isinstance(v, dict) and isinstance(base.get(k), dict):
|
|
46
|
+
deep_merge(base[k], v)
|
|
47
|
+
elif isinstance(v, list) and isinstance(base.get(k), list):
|
|
48
|
+
base[k] += [x for x in v if x not in base[k]]
|
|
49
|
+
else:
|
|
50
|
+
base[k] = v
|
|
51
|
+
return base
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---- optional hardening (only with --harden) --------------------------------
|
|
55
|
+
|
|
56
|
+
def hardening_block() -> dict:
|
|
57
|
+
"""The OPTIONAL per-tool gate + statusline. Not installed unless --harden."""
|
|
58
|
+
py = _python_cmd()
|
|
59
|
+
return {
|
|
60
|
+
"hooks": {
|
|
61
|
+
"PreToolUse": [{"matcher": "*", "hooks": [
|
|
62
|
+
{"type": "command", "command": f'{py} "{_p("hooks", "goal_policy.py")}"', "timeout": 10}]}],
|
|
63
|
+
},
|
|
64
|
+
"statusLine": {"type": "command", "command": f'{py} "{_p("statusline", "goal_status.py")}"'},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---- MCP dependency provisioning --------------------------------------------
|
|
69
|
+
|
|
70
|
+
def _venv_python(venv: Path) -> Path:
|
|
71
|
+
sub = "Scripts" if sys.platform == "win32" else "bin"
|
|
72
|
+
exe = "python.exe" if sys.platform == "win32" else "python"
|
|
73
|
+
return venv / sub / exe
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# The server imports `mcp.server.fastmcp`, which MCP SDK 2.x no longer ships.
|
|
77
|
+
# Checking a bare `import mcp` accepts an interpreter that cannot actually run
|
|
78
|
+
# the server, so registration would write a config that fails to start —
|
|
79
|
+
# `cursor-agent mcp list` reports "Connection failed" and Claude silently drops
|
|
80
|
+
# the tools. Probe the module the server really needs.
|
|
81
|
+
MCP_PROBE = "import mcp.server.fastmcp"
|
|
82
|
+
# Upper bound for the same reason; drop it once loopx_mcp.py speaks the 2.x API.
|
|
83
|
+
MCP_REQUIREMENT = "mcp<2"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _has_mcp(py) -> bool:
|
|
87
|
+
try:
|
|
88
|
+
return subprocess.run([str(py), "-c", MCP_PROBE], capture_output=True).returncode == 0
|
|
89
|
+
except (FileNotFoundError, OSError):
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def provision_mcp_python(dry: bool, allow_system_pip: bool = False) -> str:
|
|
94
|
+
"""Return an interpreter that can `import mcp`, installing it into a dedicated
|
|
95
|
+
venv if needed (works under PEP 668 externally-managed pythons).
|
|
96
|
+
|
|
97
|
+
We install `mcp` ONLY into a dedicated venv we own. We never silently mutate
|
|
98
|
+
the user's active/system interpreter (`pip install --break-system-packages`);
|
|
99
|
+
that is gated behind an explicit ``allow_system_pip`` opt-in."""
|
|
100
|
+
if _has_mcp(sys.executable):
|
|
101
|
+
print(f"[deps] mcp already importable by {sys.executable}")
|
|
102
|
+
return sys.executable
|
|
103
|
+
vpy = _venv_python(MCP_VENV)
|
|
104
|
+
if _has_mcp(vpy):
|
|
105
|
+
print(f"[deps] reusing mcp venv {MCP_VENV}")
|
|
106
|
+
return str(vpy)
|
|
107
|
+
if dry:
|
|
108
|
+
print(f"[deps] (dry-run) would create venv {MCP_VENV} and pip install mcp")
|
|
109
|
+
return str(vpy)
|
|
110
|
+
print(f"[deps] creating mcp venv {MCP_VENV} …")
|
|
111
|
+
MCP_VENV.parent.mkdir(parents=True, exist_ok=True)
|
|
112
|
+
r = subprocess.run([sys.executable, "-m", "venv", str(MCP_VENV)], capture_output=True, text=True)
|
|
113
|
+
if r.returncode == 0:
|
|
114
|
+
subprocess.run([str(vpy), "-m", "pip", "install", "-q", "--upgrade", "pip"], capture_output=True, text=True)
|
|
115
|
+
pip = subprocess.run([str(vpy), "-m", "pip", "install", "-q", MCP_REQUIREMENT], capture_output=True, text=True)
|
|
116
|
+
if pip.returncode == 0 and _has_mcp(vpy):
|
|
117
|
+
print(f"[deps] mcp installed into {MCP_VENV}")
|
|
118
|
+
return str(vpy)
|
|
119
|
+
print("[deps] venv pip install failed:\n" + (pip.stdout + pip.stderr)[:300])
|
|
120
|
+
else:
|
|
121
|
+
print("[deps] venv creation failed:\n" + (r.stdout + r.stderr)[:300])
|
|
122
|
+
# The dedicated venv is the only place we install `mcp`. Do NOT touch the
|
|
123
|
+
# user's active/system interpreter unless they explicitly opted in.
|
|
124
|
+
if allow_system_pip:
|
|
125
|
+
print("[deps] --allow-system-pip: pip install --break-system-packages mcp")
|
|
126
|
+
subprocess.run([sys.executable, "-m", "pip", "install", "-q", "--break-system-packages", MCP_REQUIREMENT],
|
|
127
|
+
capture_output=True, text=True)
|
|
128
|
+
if _has_mcp(sys.executable):
|
|
129
|
+
return sys.executable
|
|
130
|
+
print("[deps] WARNING: could not provision `mcp` into a dedicated venv, and we will\n"
|
|
131
|
+
" NOT modify your system Python. The loopx MCP tools will not load until\n"
|
|
132
|
+
" you install `mcp` yourself, e.g.:\n"
|
|
133
|
+
f" {sys.executable} -m venv {MCP_VENV} && {vpy} -m pip install '{MCP_REQUIREMENT}'\n"
|
|
134
|
+
" (or re-run install.py with --allow-system-pip to use the active interpreter).")
|
|
135
|
+
return _python_cmd()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def install_mcp(
|
|
139
|
+
dry: bool,
|
|
140
|
+
py: str,
|
|
141
|
+
scope: str,
|
|
142
|
+
migrate_goal_harness: bool = False,
|
|
143
|
+
project_dir: Path | None = None,
|
|
144
|
+
):
|
|
145
|
+
"""Register the loopx MCP server at the chosen scope via `claude mcp add`.
|
|
146
|
+
|
|
147
|
+
We replace only OUR `loopx` entry. A legacy/user-owned `goal-harness` MCP is
|
|
148
|
+
left untouched by default — we don't delete a server we don't own. If one
|
|
149
|
+
exists we print a manual cleanup hint; `--migrate-goal-harness` removes it."""
|
|
150
|
+
if scope == "project" and project_dir is None:
|
|
151
|
+
raise ValueError("project_dir is required for project-scoped MCP registration")
|
|
152
|
+
cwd_run = str(project_dir) if scope == "project" else None
|
|
153
|
+
claude = shutil.which("claude")
|
|
154
|
+
mcp_path = _p("mcp", "loopx_mcp.py")
|
|
155
|
+
add = ["mcp", "add", "--scope", scope, "loopx", "--", py, mcp_path]
|
|
156
|
+
if not claude:
|
|
157
|
+
print("[mcp] claude CLI not on PATH — add the MCP server manually:\n claude " + " ".join(add))
|
|
158
|
+
return
|
|
159
|
+
print(f"[mcp] claude mcp add --scope {scope} loopx -- {py} <loopx_mcp.py>")
|
|
160
|
+
if dry:
|
|
161
|
+
return
|
|
162
|
+
subprocess.run(
|
|
163
|
+
[claude, "mcp", "remove", "--scope", scope, "loopx"],
|
|
164
|
+
capture_output=True,
|
|
165
|
+
text=True,
|
|
166
|
+
cwd=cwd_run,
|
|
167
|
+
)
|
|
168
|
+
has_gh = subprocess.run(
|
|
169
|
+
[claude, "mcp", "get", "goal-harness"],
|
|
170
|
+
capture_output=True,
|
|
171
|
+
text=True,
|
|
172
|
+
cwd=cwd_run,
|
|
173
|
+
).returncode == 0
|
|
174
|
+
if has_gh:
|
|
175
|
+
if migrate_goal_harness:
|
|
176
|
+
print(f"[mcp] --migrate-goal-harness: removing legacy goal-harness MCP at {scope} scope")
|
|
177
|
+
subprocess.run(
|
|
178
|
+
[claude, "mcp", "remove", "--scope", scope, "goal-harness"],
|
|
179
|
+
capture_output=True,
|
|
180
|
+
text=True,
|
|
181
|
+
cwd=cwd_run,
|
|
182
|
+
)
|
|
183
|
+
else:
|
|
184
|
+
print("[mcp] note: a legacy `goal-harness` MCP entry exists — left untouched. Remove it "
|
|
185
|
+
f"yourself with `claude mcp remove --scope {scope} goal-harness`, or re-run with "
|
|
186
|
+
"--migrate-goal-harness.")
|
|
187
|
+
r = subprocess.run(
|
|
188
|
+
[claude, *add],
|
|
189
|
+
capture_output=True,
|
|
190
|
+
text=True,
|
|
191
|
+
cwd=cwd_run,
|
|
192
|
+
)
|
|
193
|
+
if r.returncode != 0:
|
|
194
|
+
print(" (mcp add failed; add manually:\n claude " + " ".join(add) + "\n " + (r.stdout + r.stderr)[:200] + ")")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# ---- /loopx command ----------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
def command_md() -> str:
|
|
200
|
+
entry = _p("scripts", "goalmode_cmd.py")
|
|
201
|
+
py = _python_cmd()
|
|
202
|
+
return (
|
|
203
|
+
"---\n"
|
|
204
|
+
"description: loopx goal-mode setup (NOT Claude Code's built-in /goal). "
|
|
205
|
+
"`/loopx <task>` sets up a goal + writes .claude/loop.md; drive the loop with native /loop. "
|
|
206
|
+
"bare /loopx = arm; off | status.\n"
|
|
207
|
+
"argument-hint: <task to do> | (no args = arm) | off | status\n"
|
|
208
|
+
f"allowed-tools: Bash({py}:*)\n"
|
|
209
|
+
"---\n\n"
|
|
210
|
+
"Run the loopx setup helper and read its output:\n\n"
|
|
211
|
+
f"!`{py} \"{entry}\" $ARGUMENTS`\n\n"
|
|
212
|
+
"The output is loopx control-plane SETUP / status info — it is the COMPLETE, user-facing "
|
|
213
|
+
"result. Show it to the user VERBATIM and STOP. Do NOT do any work, claim todos, write "
|
|
214
|
+
"files, or loop here: the WORK runs when the user types Claude Code's native `/loop` (which "
|
|
215
|
+
"executes `.claude/loop.md`). Do not summarize a multi-line `status` detail block into one line.\n\n"
|
|
216
|
+
"NOTE: this is `/loopx` (loopx control-plane SETUP), NOT a runtime and NOT Claude Code's "
|
|
217
|
+
"built-in `/goal`. The run loop is native `/loop`; loopx provides the deterministic "
|
|
218
|
+
"`should_run` protocol.\n"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def install_command(dry: bool, cmd_dir: Path):
|
|
223
|
+
path = cmd_dir / "loopx.md"
|
|
224
|
+
print(f"[command] /loopx -> {path}")
|
|
225
|
+
if dry:
|
|
226
|
+
return
|
|
227
|
+
cmd_dir.mkdir(parents=True, exist_ok=True)
|
|
228
|
+
old = cmd_dir / "goalmode.md" # migrate the legacy /goalmode entry name
|
|
229
|
+
if old.exists():
|
|
230
|
+
old.unlink()
|
|
231
|
+
path.write_text(command_md(), encoding="utf-8")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def add_hardening(dry: bool, settings_path: Path):
|
|
235
|
+
"""Deep-merge the optional hook + statusline into settings_path, preserving
|
|
236
|
+
existing config. NEVER deletes permission rules."""
|
|
237
|
+
cur = {}
|
|
238
|
+
if settings_path.exists():
|
|
239
|
+
try:
|
|
240
|
+
cur = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
241
|
+
except Exception:
|
|
242
|
+
cur = {}
|
|
243
|
+
# prune prior loopx PreToolUse entries (avoid stacking on reinstall)
|
|
244
|
+
for grp in (cur.get("hooks", {}) or {}).get("PreToolUse", []) or []:
|
|
245
|
+
grp["hooks"] = [h for h in grp.get("hooks", []) if "goal_policy.py" not in str(h.get("command", ""))]
|
|
246
|
+
if isinstance(cur.get("hooks", {}).get("PreToolUse"), list):
|
|
247
|
+
cur["hooks"]["PreToolUse"] = [g for g in cur["hooks"]["PreToolUse"] if g.get("hooks")]
|
|
248
|
+
merged = deep_merge(cur, hardening_block())
|
|
249
|
+
print(f"[harden] PreToolUse should_run gate + statusline -> {settings_path}")
|
|
250
|
+
if not dry:
|
|
251
|
+
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
252
|
+
settings_path.write_text(json.dumps(merged, indent=2), encoding="utf-8")
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def warn_legacy_perms(settings_path: Path):
|
|
256
|
+
"""Detect a legacy loopx credential-deny and SUGGEST manual cleanup. Never mutate."""
|
|
257
|
+
try:
|
|
258
|
+
cur = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
259
|
+
except Exception:
|
|
260
|
+
return
|
|
261
|
+
deny = ((cur.get("permissions") or {}).get("deny")) or []
|
|
262
|
+
legacy = [r for r in deny if r in ("Read(~/.ssh/**)", "Read(~/.aws/**)")]
|
|
263
|
+
if legacy:
|
|
264
|
+
print(f"[note] {settings_path} has loopx-era permission deny rules {legacy}. "
|
|
265
|
+
"We no longer add these and will NOT remove them automatically. "
|
|
266
|
+
"To drop them, edit that file's `permissions.deny` by hand.")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def main():
|
|
270
|
+
ap = argparse.ArgumentParser()
|
|
271
|
+
ap.add_argument("--scope", choices=["user", "project"], required=True,
|
|
272
|
+
help="user = ~/.claude (all projects); project = THIS project's .claude (preferred)")
|
|
273
|
+
ap.add_argument("--project", default=None, help="project dir for --scope project (default: cwd)")
|
|
274
|
+
ap.add_argument("--harden", action="store_true",
|
|
275
|
+
help="ALSO install the optional PreToolUse should_run gate + statusline")
|
|
276
|
+
ap.add_argument("--skip-mcp", action="store_true",
|
|
277
|
+
help="skip MCP venv provisioning + `claude mcp add` (for tests/CI)")
|
|
278
|
+
ap.add_argument("--allow-system-pip", action="store_true",
|
|
279
|
+
help="if the dedicated mcp venv can't be built, fall back to "
|
|
280
|
+
"`pip install --break-system-packages mcp` into the active interpreter "
|
|
281
|
+
"(off by default — we don't mutate your system Python)")
|
|
282
|
+
ap.add_argument("--migrate-goal-harness", action="store_true",
|
|
283
|
+
help="also remove a legacy `goal-harness` MCP entry at this scope "
|
|
284
|
+
"(off by default — we don't delete MCP servers we don't own)")
|
|
285
|
+
ap.add_argument("--dry-run", action="store_true")
|
|
286
|
+
a = ap.parse_args()
|
|
287
|
+
dry = a.dry_run
|
|
288
|
+
|
|
289
|
+
project_dir = None
|
|
290
|
+
if a.scope == "project":
|
|
291
|
+
project_dir = Path(a.project).resolve() if a.project else Path.cwd()
|
|
292
|
+
claude_dir = project_dir / ".claude"
|
|
293
|
+
print(f"[scope] PROJECT — installs into {claude_dir} (this project only)")
|
|
294
|
+
else:
|
|
295
|
+
claude_dir = Path.home() / ".claude"
|
|
296
|
+
print("[scope] USER — installs into ~/.claude. The /loopx command and MCP become")
|
|
297
|
+
print(" available in ALL your Claude Code projects." +
|
|
298
|
+
(" --harden adds USER-LEVEL, matcher-wide hooks too." if a.harden else ""))
|
|
299
|
+
|
|
300
|
+
if a.skip_mcp:
|
|
301
|
+
print("[mcp] skipped (--skip-mcp)")
|
|
302
|
+
else:
|
|
303
|
+
mcp_python = provision_mcp_python(dry, a.allow_system_pip)
|
|
304
|
+
install_mcp(
|
|
305
|
+
dry,
|
|
306
|
+
mcp_python,
|
|
307
|
+
a.scope,
|
|
308
|
+
a.migrate_goal_harness,
|
|
309
|
+
project_dir=project_dir,
|
|
310
|
+
)
|
|
311
|
+
install_command(dry, claude_dir / "commands")
|
|
312
|
+
|
|
313
|
+
settings_path = claude_dir / "settings.json"
|
|
314
|
+
if a.harden:
|
|
315
|
+
add_hardening(dry, settings_path)
|
|
316
|
+
else:
|
|
317
|
+
print("[harden] skipped (default): no hooks/statusline installed. Add later with --harden.")
|
|
318
|
+
warn_legacy_perms(settings_path)
|
|
319
|
+
|
|
320
|
+
print("\nloopx Claude adapter installed.")
|
|
321
|
+
print("Next: in a project with a goal, type `/loopx <task>`, then drive it with native `/loop`.")
|
|
322
|
+
print("Restart any open Claude Code session so the command/MCP load.")
|
|
323
|
+
if dry:
|
|
324
|
+
print("(dry-run: nothing written)")
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
if __name__ == "__main__":
|
|
328
|
+
main()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Statusline segment: a glanceable loopx goal state for the Claude Code CLI.
|
|
3
|
+
|
|
4
|
+
Claude Code feeds session JSON on stdin; we resolve THIS project's goal (only
|
|
5
|
+
while goal-mode is armed) and run one quick `loopx quota should-run` probe, then
|
|
6
|
+
render a single compact line. We surface the signals a watching user actually
|
|
7
|
+
needs, in priority order:
|
|
8
|
+
|
|
9
|
+
1. ⚠ needs you — an operator gate / open user todo is blocking (highest signal)
|
|
10
|
+
2. ▶ working — should_run=true, plus the next concrete action loopx picked
|
|
11
|
+
3. ⏸ <state> — paused/holding, plus the short reason (quota, gate, health…)
|
|
12
|
+
|
|
13
|
+
…always tailed with todo progress (done✓/open▸). Prints nothing when there is no
|
|
14
|
+
goal here or goal-mode is off.
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
# Share the registry-driven resolver with the hooks so the statusline shows THIS
|
|
24
|
+
# session's project goal, and only while goal-mode is armed (heartbeat installed).
|
|
25
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "hooks"))
|
|
26
|
+
from goal_state import active_context
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
import shutil as _shutil
|
|
30
|
+
def _gh_prefix():
|
|
31
|
+
_exe = _shutil.which("loopx")
|
|
32
|
+
return [_exe] if _exe else [__import__("sys").executable, "-m", "loopx.cli"]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _clip(s, n: int) -> str:
|
|
36
|
+
s = " ".join(str(s or "").split()) # collapse whitespace/newlines
|
|
37
|
+
return s if len(s) <= n else s[: n - 1] + "…"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _render(gid: str, d: dict) -> str:
|
|
41
|
+
"""Turn a `quota should-run` payload into one compact statusline string."""
|
|
42
|
+
agent = d.get("agent_todo_summary") or {}
|
|
43
|
+
done, open_ = agent.get("done_count"), agent.get("open_count")
|
|
44
|
+
prog = f"{done}✓/{open_}▸" if (done is not None or open_ is not None) else ""
|
|
45
|
+
|
|
46
|
+
ic = d.get("interaction_contract") or {}
|
|
47
|
+
user_ch = ic.get("user_channel") or {}
|
|
48
|
+
users = d.get("user_todo_summary") or {}
|
|
49
|
+
gate = d.get("gate_prompt")
|
|
50
|
+
needs_user = bool(gate) or bool(user_ch.get("action_required")) or bool(users.get("open_count"))
|
|
51
|
+
|
|
52
|
+
if needs_user:
|
|
53
|
+
msg = gate or f"{users.get('open_count') or 'a'} user todo(s) to answer"
|
|
54
|
+
return f"[loopx {gid} · ⚠ needs you: {_clip(msg, 46)}]"
|
|
55
|
+
|
|
56
|
+
if d.get("should_run") is True:
|
|
57
|
+
nxt = d.get("recommended_action")
|
|
58
|
+
head = f"▶ {prog}".rstrip() if prog else "▶ working"
|
|
59
|
+
return f"[loopx {gid} · {head}" + (f" · next: {_clip(nxt, 40)}]" if nxt else "]")
|
|
60
|
+
|
|
61
|
+
# paused / holding: lead with WHY (state + short reason), then progress
|
|
62
|
+
state = (d.get("state") or "paused").strip()
|
|
63
|
+
reason = d.get("reason")
|
|
64
|
+
tail = f" · {prog}" if prog else ""
|
|
65
|
+
return f"[loopx {gid} · ⏸ {state}" + (f": {_clip(reason, 38)}" if reason else "") + f"{tail}]"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def main():
|
|
69
|
+
raw = sys.stdin.read() # Claude's session json carries this session's cwd
|
|
70
|
+
cwd = None
|
|
71
|
+
try:
|
|
72
|
+
sess = json.loads(raw or "{}")
|
|
73
|
+
cwd = sess.get("cwd") or (sess.get("workspace") or {}).get("current_dir")
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
76
|
+
st = active_context(cwd or Path.cwd())
|
|
77
|
+
if not st:
|
|
78
|
+
print("") # no goal here, or goal-mode off -> no segment
|
|
79
|
+
return
|
|
80
|
+
gid = st.get("goal_id", "?")
|
|
81
|
+
try:
|
|
82
|
+
cmd = list(_gh_prefix())
|
|
83
|
+
if st.get("registry"):
|
|
84
|
+
cmd += ["--registry", st["registry"]]
|
|
85
|
+
cmd += ["--format", "json", "quota", "should-run", "--goal-id", gid]
|
|
86
|
+
if st.get("agent_id"):
|
|
87
|
+
cmd += ["--agent-id", st["agent_id"]]
|
|
88
|
+
out = subprocess.run(cmd, capture_output=True, text=True, timeout=8)
|
|
89
|
+
d = json.loads(out.stdout or "{}")
|
|
90
|
+
print(_render(gid, d))
|
|
91
|
+
except Exception:
|
|
92
|
+
# probe failed: still show that goal-mode is armed, without counts/state
|
|
93
|
+
print(f"[loopx {gid}]")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
main()
|