okstra 0.186.7 → 0.187.0
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.
- package/README.md +1 -1
- package/dist/cli-registry.d.mts +88 -1
- package/dist/cli-registry.mjs +68 -111
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.mjs +0 -1
- package/dist/commands/execute/render-bundle.mjs.map +1 -1
- package/dist/commands/execute/run.mjs +8 -3
- package/dist/commands/execute/run.mjs.map +1 -1
- package/dist/commands/lifecycle/check-project.mjs +1 -14
- package/dist/commands/lifecycle/check-project.mjs.map +1 -1
- package/dist/commands/lifecycle/config.mjs +38 -40
- package/dist/commands/lifecycle/config.mjs.map +1 -1
- package/dist/commands/lifecycle/doctor.d.mts +22 -7
- package/dist/commands/lifecycle/doctor.mjs +77 -49
- package/dist/commands/lifecycle/doctor.mjs.map +1 -1
- package/dist/commands/lifecycle/install.d.mts +12 -10
- package/dist/commands/lifecycle/install.mjs +104 -39
- package/dist/commands/lifecycle/install.mjs.map +1 -1
- package/dist/commands/lifecycle/paths.mjs +8 -3
- package/dist/commands/lifecycle/paths.mjs.map +1 -1
- package/dist/commands/lifecycle/preflight.mjs +2 -1
- package/dist/commands/lifecycle/preflight.mjs.map +1 -1
- package/dist/commands/lifecycle/setup.mjs +22 -36
- package/dist/commands/lifecycle/setup.mjs.map +1 -1
- package/dist/commands/lifecycle/uninstall.d.mts +4 -2
- package/dist/commands/lifecycle/uninstall.mjs +59 -15
- package/dist/commands/lifecycle/uninstall.mjs.map +1 -1
- package/dist/commands/memory/memory.mjs +7 -1
- package/dist/commands/memory/memory.mjs.map +1 -1
- package/dist/lib/helper-scripts.d.mts +1 -1
- package/dist/lib/helper-scripts.mjs +10 -18
- package/dist/lib/helper-scripts.mjs.map +1 -1
- package/dist/lib/host-config.d.mts +72 -0
- package/dist/lib/host-config.mjs +404 -0
- package/dist/lib/host-config.mjs.map +1 -0
- package/dist/lib/host-registry-client.d.mts +2 -2
- package/dist/lib/host-registry-client.mjs +0 -3
- package/dist/lib/host-registry-client.mjs.map +1 -1
- package/dist/lib/install-assets.d.mts +1 -0
- package/dist/lib/install-assets.mjs +4 -0
- package/dist/lib/install-assets.mjs.map +1 -1
- package/dist/lib/proc.d.mts +2 -0
- package/dist/lib/proc.mjs +12 -0
- package/dist/lib/proc.mjs.map +1 -1
- package/dist/lib/python-command.d.mts +2 -0
- package/dist/lib/python-command.mjs +52 -0
- package/dist/lib/python-command.mjs.map +1 -0
- package/dist/lib/python-helper.d.mts +2 -5
- package/dist/lib/python-helper.mjs +3 -52
- package/dist/lib/python-helper.mjs.map +1 -1
- package/dist/lib/runtime-payload.d.mts +23 -0
- package/dist/lib/runtime-payload.mjs +57 -0
- package/dist/lib/runtime-payload.mjs.map +1 -0
- package/dist/lib/types.d.mts +24 -13
- package/docs/architecture/storage-model.md +21 -5
- package/docs/architecture.md +37 -36
- package/docs/cli.md +55 -76
- package/docs/coding-rules.md +295 -0
- package/docs/container.md +10 -36
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/for-ai/skills/okstra-code-review.md +0 -1
- package/docs/for-ai/skills/okstra-container-build.md +8 -41
- package/docs/for-ai/skills/okstra-inspect.md +4 -4
- package/docs/for-ai/skills/okstra-manager.md +2 -2
- package/docs/for-ai/skills/okstra-rollup.md +0 -1
- package/docs/for-ai/skills/okstra-run.md +3 -3
- package/docs/for-ai/skills/okstra-user-response.md +0 -1
- package/docs/performance-improvement-plan-v2.md +1 -1
- package/docs/project-structure-overview.md +62 -62
- package/docs/task-process/README.md +3 -3
- package/docs/task-process/common-flow.md +1 -1
- package/docs/task-process/error-analysis.md +3 -3
- package/docs/task-process/implementation-planning.md +3 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +3 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +6 -11
- package/runtime/agents/workers/report-writer-worker.md +1 -1
- package/runtime/bin/lib/okstra/cli.sh +4 -0
- package/runtime/bin/lib/okstra/globals.sh +2 -0
- package/runtime/bin/lib/okstra/interactive.sh +26 -173
- package/runtime/bin/lib/okstra/project-resolver.sh +23 -61
- package/runtime/bin/lib/okstra/usage.sh +3 -3
- package/runtime/bin/okstra-compact-reminder.sh +1 -5
- package/runtime/bin/okstra-error-log.py +19 -2
- package/runtime/bin/okstra-import-check.py +26 -0
- package/runtime/bin/okstra-inject-report-index.py +5 -4
- package/runtime/bin/okstra-provider-exec.py +22 -17
- package/runtime/bin/okstra-render-final-report.py +16 -6
- package/runtime/bin/okstra-render-report-views.py +25 -18
- package/runtime/bin/okstra-report-translate.py +30 -18
- package/runtime/bin/okstra-spawn-followups.py +4 -0
- package/runtime/bin/okstra-token-usage.py +3 -0
- package/runtime/bin/okstra.sh +4 -2
- package/runtime/bin/okstra_bootstrap.py +58 -0
- package/runtime/prompts/coding-preflight/overview.md +1 -1
- package/runtime/prompts/duties/planning-worker.md +1 -1
- package/runtime/prompts/launch.template.md +35 -9
- package/runtime/prompts/lead/convergence.md +96 -87
- package/runtime/prompts/lead/okstra-lead-contract.md +63 -12
- package/runtime/prompts/lead/plan-body-verification.md +65 -46
- package/runtime/prompts/lead/report-writer.md +34 -4
- package/runtime/prompts/lead/team-contract.md +4 -1
- package/runtime/prompts/profiles/_clarification-recommendation.md +2 -1
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +3 -3
- package/runtime/prompts/profiles/_coverage-critic.md +1 -1
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +3 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +16 -2
- package/runtime/prompts/profiles/error-analysis.md +4 -3
- package/runtime/prompts/profiles/final-verification.md +2 -2
- package/runtime/prompts/profiles/implementation-planning.md +22 -17
- package/runtime/prompts/profiles/implementation.md +3 -2
- package/runtime/prompts/profiles/requirements-discovery.md +2 -1
- package/runtime/prompts/wizard/prompts.ko.json +29 -8
- package/runtime/python/okstra_ctl/__init__.py +6 -27
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +4 -5
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +21 -2
- package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +3 -12
- package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +40 -22
- package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +14 -21
- package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +37 -20
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +86 -14
- package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +12 -12
- package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +4 -7
- package/runtime/python/okstra_ctl/agent/__init__.py +1 -0
- package/runtime/python/okstra_ctl/{agent_activity.py → agent/activity.py} +12 -1
- package/runtime/python/okstra_ctl/{agent_invocation.py → agent/invocation.py} +34 -5
- package/runtime/python/okstra_ctl/agent/prompt_cli/__init__.py +19 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/__main__.py +11 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/cli.py +215 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/dynamic_verifier.py +155 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/emit.py +66 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/inputs.py +138 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/materialize.py +408 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/results.py +197 -0
- package/runtime/python/okstra_ctl/agent/prompt_cli/run_identity.py +107 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +47 -4
- package/runtime/python/okstra_ctl/approval_decisions.py +260 -8
- package/runtime/python/okstra_ctl/assignment_resolver.py +0 -12
- package/runtime/python/okstra_ctl/attempt_evidence.py +12 -24
- package/runtime/python/okstra_ctl/blocking_checks.py +251 -0
- package/runtime/python/okstra_ctl/brief_frontmatter.py +0 -7
- package/runtime/python/okstra_ctl/clarification_items/__init__.py +102 -0
- package/runtime/python/okstra_ctl/clarification_items/carry.py +224 -0
- package/runtime/python/okstra_ctl/clarification_items/dispositions.py +135 -0
- package/runtime/python/okstra_ctl/clarification_items/parsing.py +253 -0
- package/runtime/python/okstra_ctl/clarification_items/rows.py +115 -0
- package/runtime/python/okstra_ctl/clarification_items/scan.py +215 -0
- package/runtime/python/okstra_ctl/clarification_items/sidecars.py +211 -0
- package/runtime/python/okstra_ctl/cmux.py +38 -31
- package/runtime/python/okstra_ctl/code_review_target.py +163 -1
- package/runtime/python/okstra_ctl/conformance.py +23 -0
- package/runtime/python/okstra_ctl/container.py +38 -421
- package/runtime/python/okstra_ctl/context_cost.py +16 -2
- package/runtime/python/okstra_ctl/contract_graph_cli.py +7 -0
- package/runtime/python/okstra_ctl/convergence.py +101 -15
- package/runtime/python/okstra_ctl/convergence_critic_prompt.py +345 -0
- package/runtime/python/okstra_ctl/convergence_engine.py +382 -70
- package/runtime/python/okstra_ctl/convergence_store.py +5 -29
- package/runtime/python/okstra_ctl/design_prep.py +108 -12
- package/runtime/python/okstra_ctl/design_snapshot.py +11 -1
- package/runtime/python/okstra_ctl/design_surfaces.py +29 -2
- package/runtime/python/okstra_ctl/dispatch_core.py +102 -25
- package/runtime/python/okstra_ctl/dispatch_state.py +268 -39
- package/runtime/python/okstra_ctl/doctor_cli.py +48 -0
- package/runtime/python/okstra_ctl/domain/worker_exec.py +10 -5
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +21 -1
- package/runtime/python/okstra_ctl/domain/worker_stream.py +52 -21
- package/runtime/python/okstra_ctl/domain/write_policy.py +219 -0
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +9 -2
- package/runtime/python/okstra_ctl/error_log_core.py +1 -1
- package/runtime/python/okstra_ctl/error_report.py +14 -0
- package/runtime/python/okstra_ctl/error_zip.py +18 -2
- package/runtime/python/okstra_ctl/execution_identity.py +100 -7
- package/runtime/python/okstra_ctl/execution_manifest.py +67 -20
- package/runtime/python/okstra_ctl/execution_mutation_audit.py +90 -8
- package/runtime/python/okstra_ctl/final_report_paths.py +17 -0
- package/runtime/python/okstra_ctl/final_report_schema.py +90 -16
- package/runtime/python/okstra_ctl/git_reconcile.py +22 -2
- package/runtime/python/okstra_ctl/handoff.py +24 -1
- package/runtime/python/okstra_ctl/ids.py +6 -16
- package/runtime/python/okstra_ctl/implementation_direction.py +142 -33
- package/runtime/python/okstra_ctl/implementation_options.py +25 -11
- package/runtime/python/okstra_ctl/implementation_outcome.py +5 -2
- package/runtime/python/okstra_ctl/improvement_lenses.py +0 -14
- package/runtime/python/okstra_ctl/incremental_carry.py +69 -8
- package/runtime/python/okstra_ctl/incremental_scope.py +173 -12
- package/runtime/python/okstra_ctl/index.py +2 -2
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +48 -8
- package/runtime/python/okstra_ctl/interactive_cli.py +223 -0
- package/runtime/python/okstra_ctl/json_boundary.py +10 -0
- package/runtime/python/okstra_ctl/listing.py +0 -91
- package/runtime/python/okstra_ctl/locks.py +5 -19
- package/runtime/python/okstra_ctl/log_report.py +14 -0
- package/runtime/python/okstra_ctl/manager_cli.py +22 -3
- package/runtime/python/okstra_ctl/manager_launch.py +4 -8
- package/runtime/python/okstra_ctl/material.py +2 -2
- package/runtime/python/okstra_ctl/migrate.py +23 -1
- package/runtime/python/okstra_ctl/model_cli.py +19 -5
- package/runtime/python/okstra_ctl/model_discovery.py +46 -47
- package/runtime/python/okstra_ctl/model_io/__init__.py +1 -0
- package/runtime/python/okstra_ctl/model_io/lines.py +163 -0
- package/runtime/python/okstra_ctl/model_io/references.py +370 -0
- package/runtime/python/okstra_ctl/model_io/renderers.py +498 -0
- package/runtime/python/okstra_ctl/model_io_cli.py +30 -945
- package/runtime/python/okstra_ctl/model_pool.py +10 -6
- package/runtime/python/okstra_ctl/models.py +8 -8
- package/runtime/python/okstra_ctl/mutation_recovery.py +5 -63
- package/runtime/python/okstra_ctl/next_phase.py +121 -69
- package/runtime/python/okstra_ctl/pane_reclaim.py +23 -0
- package/runtime/python/okstra_ctl/pane_title.py +11 -8
- package/runtime/python/okstra_ctl/path_hints.py +36 -11
- package/runtime/python/okstra_ctl/paths.py +76 -8
- package/runtime/python/okstra_ctl/plan_items.py +132 -16
- package/runtime/python/okstra_ctl/plan_items_cli.py +486 -36
- package/runtime/python/okstra_ctl/plan_run_root.py +1 -1
- package/runtime/python/okstra_ctl/plan_validate_cli.py +51 -0
- package/runtime/python/okstra_ctl/plan_verify_cli.py +80 -0
- package/runtime/python/okstra_ctl/prepare_error.py +20 -0
- package/runtime/python/okstra_ctl/prior_planning.py +157 -0
- package/runtime/python/okstra_ctl/profile_show.py +18 -1
- package/runtime/python/okstra_ctl/project_setup_cli.py +140 -0
- package/runtime/python/okstra_ctl/recap.py +22 -1
- package/runtime/python/okstra_ctl/reconcile.py +84 -0
- package/runtime/python/okstra_ctl/registry/host_registry.py +18 -1
- package/runtime/python/okstra_ctl/render.py +98 -32
- package/runtime/python/okstra_ctl/render_final_report.py +3 -7
- package/runtime/python/okstra_ctl/report_assembly.py +297 -7
- package/runtime/python/okstra_ctl/report_contract.py +30 -7
- package/runtime/python/okstra_ctl/report_finalize.py +206 -15
- package/runtime/python/okstra_ctl/report_html/common.py +2 -2
- package/runtime/python/okstra_ctl/report_html/render.py +8 -12
- package/runtime/python/okstra_ctl/report_language.py +8 -4
- package/runtime/python/okstra_ctl/report_narrative.py +90 -3
- package/runtime/python/okstra_ctl/report_projections.py +8 -2
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +258 -1
- package/runtime/python/okstra_ctl/report_translation.py +2 -35
- package/runtime/python/okstra_ctl/report_views.py +4 -22
- package/runtime/python/okstra_ctl/resolve_task_key.py +13 -0
- package/runtime/python/okstra_ctl/rollup.py +13 -0
- package/runtime/python/okstra_ctl/run.py +639 -138
- package/runtime/python/okstra_ctl/run_audit.py +13 -0
- package/runtime/python/okstra_ctl/run_index_row.py +0 -12
- package/runtime/python/okstra_ctl/seeding.py +21 -11
- package/runtime/python/okstra_ctl/sequence.py +1 -1
- package/runtime/python/okstra_ctl/session.py +64 -14
- package/runtime/python/okstra_ctl/set_work_status.py +18 -0
- package/runtime/python/okstra_ctl/stage_integrate.py +15 -1
- package/runtime/python/okstra_ctl/stage_ledger.py +1 -1
- package/runtime/python/okstra_ctl/stage_map.py +57 -11
- package/runtime/python/okstra_ctl/stage_map_cli.py +83 -0
- package/runtime/python/okstra_ctl/stage_map_view.py +65 -0
- package/runtime/python/okstra_ctl/stage_targets.py +3 -12
- package/runtime/python/okstra_ctl/task_list_cli.py +138 -0
- package/runtime/python/okstra_ctl/task_show_cli.py +66 -0
- package/runtime/python/okstra_ctl/task_target.py +4 -16
- package/runtime/python/okstra_ctl/team.py +49 -2
- package/runtime/python/okstra_ctl/time_report.py +17 -2
- package/runtime/python/okstra_ctl/usage_report.py +11 -0
- package/runtime/python/okstra_ctl/user_response.py +55 -218
- package/runtime/python/okstra_ctl/user_response_values.py +242 -0
- package/runtime/python/okstra_ctl/validation_contract.py +3 -0
- package/runtime/python/okstra_ctl/verification_target.py +68 -0
- package/runtime/python/okstra_ctl/wizard.py +457 -92
- package/runtime/python/okstra_ctl/worker_artifacts.py +75 -16
- package/runtime/python/okstra_ctl/worker_audit_check.py +22 -0
- package/runtime/python/okstra_ctl/worker_dispatch.py +21 -0
- package/runtime/python/okstra_ctl/worker_liveness.py +33 -0
- package/runtime/python/okstra_ctl/worker_prompt_body.py +15 -2
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +98 -7
- package/runtime/python/okstra_ctl/worker_prompt_headers.py +24 -2
- package/runtime/python/okstra_ctl/worker_prompt_policy.py +18 -3
- package/runtime/python/okstra_ctl/worker_request.py +2 -1
- package/runtime/python/okstra_ctl/worker_runner.py +16 -9
- package/runtime/python/okstra_ctl/worker_state.py +16 -1
- package/runtime/python/okstra_ctl/workflow.py +18 -1
- package/runtime/python/okstra_ctl/worktree/__init__.py +92 -0
- package/runtime/python/okstra_ctl/worktree/cleanliness.py +89 -0
- package/runtime/python/okstra_ctl/worktree/decisions.py +127 -0
- package/runtime/python/okstra_ctl/worktree/git_ops.py +165 -0
- package/runtime/python/okstra_ctl/worktree/linking.py +249 -0
- package/runtime/python/okstra_ctl/worktree/naming.py +91 -0
- package/runtime/python/okstra_ctl/worktree/provision.py +385 -0
- package/runtime/python/okstra_ctl/worktree/sync_config.py +181 -0
- package/runtime/python/okstra_ctl/worktree_cli.py +75 -0
- package/runtime/python/okstra_ctl/worktree_lookup_cli.py +39 -0
- package/runtime/python/okstra_ctl/worktree_registry.py +7 -0
- package/runtime/python/okstra_ctl/worktree_status_cli.py +53 -0
- package/runtime/python/okstra_ctl/write_policy.py +90 -213
- package/runtime/python/okstra_project/__init__.py +0 -4
- package/runtime/python/okstra_project/dirs.py +2 -2
- package/runtime/python/okstra_project/phase_pointer.py +84 -0
- package/runtime/python/okstra_project/slug.py +24 -0
- package/runtime/python/okstra_project/state.py +22 -230
- package/runtime/python/okstra_token_usage/claude.py +9 -1
- package/runtime/python/okstra_token_usage/cli.py +3 -1
- package/runtime/python/okstra_token_usage/collect.py +71 -10
- package/runtime/python/okstra_token_usage/cursor.py +2 -0
- package/runtime/python/okstra_token_usage/grok.py +1 -5
- package/runtime/python/okstra_token_usage/paths.py +26 -0
- package/runtime/python/okstra_token_usage/pricing.py +19 -7
- package/runtime/schemas/convergence-critic-results-v1.0.schema.json +5 -0
- package/runtime/schemas/execution-manifest-v2.schema.json +10 -10
- package/runtime/schemas/final-report-v2.0.schema.json +9 -26
- package/runtime/schemas/final-report-v3.0.schema.json +173 -41
- package/runtime/schemas/report-narrative-v3.0.schema.json +3 -2
- package/runtime/skills/okstra-brief-gen/SKILL.md +35 -2
- package/runtime/skills/okstra-container-build/SKILL.md +16 -47
- package/runtime/skills/okstra-inspect/facets/history.md +1 -1
- package/runtime/skills/okstra-inspect/facets/status.md +7 -6
- package/runtime/skills/okstra-pr-gen/SKILL.md +1 -1
- package/runtime/skills/okstra-run/SKILL.md +7 -5
- package/runtime/templates/report-writer-prompt-preamble.md +1 -1
- package/runtime/templates/reports/brief.template.md +6 -2
- package/runtime/templates/reports/error-analysis-input.template.md +2 -0
- package/runtime/templates/reports/final-verification-input.template.md +2 -0
- package/runtime/templates/reports/implementation-input.template.md +7 -1
- package/runtime/templates/reports/implementation-planning-input.template.md +4 -0
- package/runtime/templates/reports/quick-input.template.md +2 -0
- package/runtime/templates/reports/release-handoff-input.template.md +6 -1
- package/runtime/templates/reports/report.js +20 -3
- package/runtime/templates/reports/schedule.template.md +2 -0
- package/runtime/templates/reports/settings.template.json +0 -10
- package/runtime/templates/reports/task-brief.template.md +3 -0
- package/runtime/templates/reports/user-response.template.md +7 -3
- package/runtime/validators/checks/fixtures-01.py +57 -0
- package/runtime/validators/checks/fixtures-02.py +565 -0
- package/runtime/validators/checks/runners-01.py +108 -0
- package/runtime/validators/checks/validate-assets-01.py +60 -0
- package/runtime/validators/checks/validate-prompt-metadata-01.py +261 -0
- package/runtime/validators/checks/validate-tasks-01.py +46 -0
- package/runtime/validators/checks/validate-tasks-02.py +85 -0
- package/runtime/validators/checks/validate-tasks-03.py +61 -0
- package/runtime/validators/checks/validate-tasks-04.py +118 -0
- package/runtime/validators/forbidden_actions.py +73 -3
- package/runtime/validators/lib/common.sh +5 -0
- package/runtime/validators/lib/fixtures.sh +7 -591
- package/runtime/validators/lib/paths.sh +13 -0
- package/runtime/validators/lib/runners.sh +6 -104
- package/runtime/validators/lib/summary.sh +1 -1
- package/runtime/validators/lib/validate-assets.sh +6 -56
- package/runtime/validators/lib/validate-prompt-metadata.sh +6 -257
- package/runtime/validators/lib/validate-tasks.sh +9 -294
- package/runtime/validators/validate-implementation-plan-stages.py +4 -4
- package/runtime/validators/validate-run.py +1369 -2654
- package/runtime/validators/validate-workflow.sh +56 -16
- package/runtime/validators/validate_improvement_report.py +2 -1
- package/runtime/validators/validate_session_conformance.py +295 -49
- package/dist/commands/execute/agent-prompt.d.mts +0 -1
- package/dist/commands/execute/agent-prompt.mjs +0 -24
- package/dist/commands/execute/agent-prompt.mjs.map +0 -1
- package/dist/commands/execute/codex-dispatch.d.mts +0 -3
- package/dist/commands/execute/codex-dispatch.mjs +0 -6
- package/dist/commands/execute/codex-dispatch.mjs.map +0 -1
- package/dist/commands/execute/codex-run.d.mts +0 -3
- package/dist/commands/execute/codex-run.mjs +0 -62
- package/dist/commands/execute/codex-run.mjs.map +0 -1
- package/dist/commands/execute/convergence.d.mts +0 -1
- package/dist/commands/execute/convergence.mjs +0 -38
- package/dist/commands/execute/convergence.mjs.map +0 -1
- package/dist/commands/execute/error-log.d.mts +0 -1
- package/dist/commands/execute/error-log.mjs +0 -18
- package/dist/commands/execute/error-log.mjs.map +0 -1
- package/dist/commands/execute/git-reconcile.d.mts +0 -1
- package/dist/commands/execute/git-reconcile.mjs +0 -30
- package/dist/commands/execute/git-reconcile.mjs.map +0 -1
- package/dist/commands/execute/handoff.d.mts +0 -1
- package/dist/commands/execute/handoff.mjs +0 -31
- package/dist/commands/execute/handoff.mjs.map +0 -1
- package/dist/commands/execute/incremental-carry.d.mts +0 -1
- package/dist/commands/execute/incremental-carry.mjs +0 -20
- package/dist/commands/execute/incremental-carry.mjs.map +0 -1
- package/dist/commands/execute/incremental-scope.d.mts +0 -1
- package/dist/commands/execute/incremental-scope.mjs +0 -29
- package/dist/commands/execute/incremental-scope.mjs.map +0 -1
- package/dist/commands/execute/integrate-stages.d.mts +0 -1
- package/dist/commands/execute/integrate-stages.mjs +0 -24
- package/dist/commands/execute/integrate-stages.mjs.map +0 -1
- package/dist/commands/execute/pane-title.d.mts +0 -1
- package/dist/commands/execute/pane-title.mjs +0 -20
- package/dist/commands/execute/pane-title.mjs.map +0 -1
- package/dist/commands/execute/plan-items.d.mts +0 -1
- package/dist/commands/execute/plan-items.mjs +0 -9
- package/dist/commands/execute/plan-items.mjs.map +0 -1
- package/dist/commands/execute/plan-validate.d.mts +0 -1
- package/dist/commands/execute/plan-validate.mjs +0 -68
- package/dist/commands/execute/plan-validate.mjs.map +0 -1
- package/dist/commands/execute/plan-verify.d.mts +0 -1
- package/dist/commands/execute/plan-verify.mjs +0 -43
- package/dist/commands/execute/plan-verify.mjs.map +0 -1
- package/dist/commands/execute/spawn-followups.d.mts +0 -1
- package/dist/commands/execute/spawn-followups.mjs +0 -22
- package/dist/commands/execute/spawn-followups.mjs.map +0 -1
- package/dist/commands/execute/team.d.mts +0 -3
- package/dist/commands/execute/team.mjs +0 -66
- package/dist/commands/execute/team.mjs.map +0 -1
- package/dist/commands/execute/token-usage.d.mts +0 -1
- package/dist/commands/execute/token-usage.mjs +0 -19
- package/dist/commands/execute/token-usage.mjs.map +0 -1
- package/dist/commands/execute/worker-audit-check.d.mts +0 -1
- package/dist/commands/execute/worker-audit-check.mjs +0 -34
- package/dist/commands/execute/worker-audit-check.mjs.map +0 -1
- package/dist/commands/execute/worker-dispatch.d.mts +0 -7
- package/dist/commands/execute/worker-dispatch.mjs +0 -64
- package/dist/commands/execute/worker-dispatch.mjs.map +0 -1
- package/dist/commands/execute/worker-state.d.mts +0 -1
- package/dist/commands/execute/worker-state.mjs +0 -28
- package/dist/commands/execute/worker-state.mjs.map +0 -1
- package/dist/commands/execute/worktree-lookup.d.mts +0 -1
- package/dist/commands/execute/worktree-lookup.mjs +0 -92
- package/dist/commands/execute/worktree-lookup.mjs.map +0 -1
- package/dist/commands/execute/worktree-status.d.mts +0 -1
- package/dist/commands/execute/worktree-status.mjs +0 -121
- package/dist/commands/execute/worktree-status.mjs.map +0 -1
- package/dist/commands/inspect/code-review.d.mts +0 -1
- package/dist/commands/inspect/code-review.mjs +0 -32
- package/dist/commands/inspect/code-review.mjs.map +0 -1
- package/dist/commands/inspect/container.d.mts +0 -1
- package/dist/commands/inspect/container.mjs +0 -25
- package/dist/commands/inspect/container.mjs.map +0 -1
- package/dist/commands/inspect/context-cost.d.mts +0 -1
- package/dist/commands/inspect/context-cost.mjs +0 -25
- package/dist/commands/inspect/context-cost.mjs.map +0 -1
- package/dist/commands/inspect/design-prep.d.mts +0 -1
- package/dist/commands/inspect/design-prep.mjs +0 -22
- package/dist/commands/inspect/design-prep.mjs.map +0 -1
- package/dist/commands/inspect/error-report.d.mts +0 -1
- package/dist/commands/inspect/error-report.mjs +0 -25
- package/dist/commands/inspect/error-report.mjs.map +0 -1
- package/dist/commands/inspect/error-zip.d.mts +0 -1
- package/dist/commands/inspect/error-zip.mjs +0 -24
- package/dist/commands/inspect/error-zip.mjs.map +0 -1
- package/dist/commands/inspect/log-report.d.mts +0 -1
- package/dist/commands/inspect/log-report.mjs +0 -26
- package/dist/commands/inspect/log-report.mjs.map +0 -1
- package/dist/commands/inspect/model-io.d.mts +0 -1
- package/dist/commands/inspect/model-io.mjs +0 -25
- package/dist/commands/inspect/model-io.mjs.map +0 -1
- package/dist/commands/inspect/profile-show.d.mts +0 -1
- package/dist/commands/inspect/profile-show.mjs +0 -28
- package/dist/commands/inspect/profile-show.mjs.map +0 -1
- package/dist/commands/inspect/recap.d.mts +0 -1
- package/dist/commands/inspect/recap.mjs +0 -30
- package/dist/commands/inspect/recap.mjs.map +0 -1
- package/dist/commands/inspect/resolve-task-key.d.mts +0 -1
- package/dist/commands/inspect/resolve-task-key.mjs +0 -24
- package/dist/commands/inspect/resolve-task-key.mjs.map +0 -1
- package/dist/commands/inspect/rollup.d.mts +0 -1
- package/dist/commands/inspect/rollup.mjs +0 -25
- package/dist/commands/inspect/rollup.mjs.map +0 -1
- package/dist/commands/inspect/run-audit.d.mts +0 -1
- package/dist/commands/inspect/run-audit.mjs +0 -25
- package/dist/commands/inspect/run-audit.mjs.map +0 -1
- package/dist/commands/inspect/set-work-status.d.mts +0 -1
- package/dist/commands/inspect/set-work-status.mjs +0 -29
- package/dist/commands/inspect/set-work-status.mjs.map +0 -1
- package/dist/commands/inspect/stage-map.d.mts +0 -1
- package/dist/commands/inspect/stage-map.mjs +0 -131
- package/dist/commands/inspect/stage-map.mjs.map +0 -1
- package/dist/commands/inspect/task-list.d.mts +0 -1
- package/dist/commands/inspect/task-list.mjs +0 -149
- package/dist/commands/inspect/task-list.mjs.map +0 -1
- package/dist/commands/inspect/task-show.d.mts +0 -1
- package/dist/commands/inspect/task-show.mjs +0 -108
- package/dist/commands/inspect/task-show.mjs.map +0 -1
- package/dist/commands/inspect/time-report.d.mts +0 -1
- package/dist/commands/inspect/time-report.mjs +0 -24
- package/dist/commands/inspect/time-report.mjs.map +0 -1
- package/dist/commands/inspect/usage-report.d.mts +0 -1
- package/dist/commands/inspect/usage-report.mjs +0 -23
- package/dist/commands/inspect/usage-report.mjs.map +0 -1
- package/dist/commands/inspect/user-response.d.mts +0 -1
- package/dist/commands/inspect/user-response.mjs +0 -35
- package/dist/commands/inspect/user-response.mjs.map +0 -1
- package/dist/commands/inspect/worker-liveness.d.mts +0 -1
- package/dist/commands/inspect/worker-liveness.mjs +0 -45
- package/dist/commands/inspect/worker-liveness.mjs.map +0 -1
- package/dist/commands/lifecycle/contract-check.d.mts +0 -1
- package/dist/commands/lifecycle/contract-check.mjs +0 -18
- package/dist/commands/lifecycle/contract-check.mjs.map +0 -1
- package/dist/commands/lifecycle/migrate.d.mts +0 -1
- package/dist/commands/lifecycle/migrate.mjs +0 -30
- package/dist/commands/lifecycle/migrate.mjs.map +0 -1
- package/dist/commands/lifecycle/model.d.mts +0 -1
- package/dist/commands/lifecycle/model.mjs +0 -22
- package/dist/commands/lifecycle/model.mjs.map +0 -1
- package/dist/commands/manager.d.mts +0 -3
- package/dist/commands/manager.mjs +0 -50
- package/dist/commands/manager.mjs.map +0 -1
- package/dist/commands/report/agent-activity.d.mts +0 -1
- package/dist/commands/report/agent-activity.mjs +0 -20
- package/dist/commands/report/agent-activity.mjs.map +0 -1
- package/dist/commands/report/approval-decision.d.mts +0 -1
- package/dist/commands/report/approval-decision.mjs +0 -21
- package/dist/commands/report/approval-decision.mjs.map +0 -1
- package/dist/commands/report/design-snapshot.d.mts +0 -1
- package/dist/commands/report/design-snapshot.mjs +0 -19
- package/dist/commands/report/design-snapshot.mjs.map +0 -1
- package/dist/commands/report/finalize.d.mts +0 -4
- package/dist/commands/report/finalize.mjs +0 -64
- package/dist/commands/report/finalize.mjs.map +0 -1
- package/dist/commands/report/inject-report-index.d.mts +0 -1
- package/dist/commands/report/inject-report-index.mjs +0 -21
- package/dist/commands/report/inject-report-index.mjs.map +0 -1
- package/dist/commands/report/render-final-report.d.mts +0 -1
- package/dist/commands/report/render-final-report.mjs +0 -23
- package/dist/commands/report/render-final-report.mjs.map +0 -1
- package/dist/commands/report/render-views.d.mts +0 -1
- package/dist/commands/report/render-views.mjs +0 -27
- package/dist/commands/report/render-views.mjs.map +0 -1
- package/dist/commands/report/translate.d.mts +0 -1
- package/dist/commands/report/translate.mjs +0 -33
- package/dist/commands/report/translate.mjs.map +0 -1
- package/runtime/bin/lib/okstra/tmux-pane.sh +0 -40
- package/runtime/bin/lib/okstra-ctl/cmd-batch.sh +0 -59
- package/runtime/bin/lib/okstra-ctl/cmd-list.sh +0 -35
- package/runtime/bin/lib/okstra-ctl/cmd-open.sh +0 -36
- package/runtime/bin/lib/okstra-ctl/cmd-projects.sh +0 -26
- package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +0 -29
- package/runtime/bin/lib/okstra-ctl/cmd-reindex.sh +0 -38
- package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +0 -345
- package/runtime/bin/lib/okstra-ctl/cmd-show.sh +0 -27
- package/runtime/bin/lib/okstra-ctl/cmd-tail.sh +0 -92
- package/runtime/bin/lib/okstra-ctl/main.sh +0 -41
- package/runtime/bin/lib/okstra-ctl/prepare.sh +0 -31
- package/runtime/bin/lib/okstra-ctl/usage.sh +0 -23
- package/runtime/bin/okstra-central.sh +0 -152
- package/runtime/bin/okstra-incremental-carry.py +0 -10
- package/runtime/bin/okstra-incremental-scope.py +0 -10
- package/runtime/bin/okstra-team-reconcile.sh +0 -36
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +0 -1156
- package/runtime/python/okstra_ctl/batch.py +0 -60
- package/runtime/python/okstra_ctl/clarification_items.py +0 -1050
- package/runtime/python/okstra_ctl/container_registry.py +0 -72
- package/runtime/python/okstra_ctl/improvement_assignment.py +0 -61
- package/runtime/python/okstra_ctl/resolver.py +0 -54
- package/runtime/python/okstra_ctl/team_reconcile.py +0 -275
- package/runtime/python/okstra_ctl/tmux.py +0 -134
- package/runtime/python/okstra_ctl/worktree.py +0 -1099
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Per-task git worktree provisioning.
|
|
2
|
+
|
|
3
|
+
Every okstra task — regardless of task-type/phase — runs inside an
|
|
4
|
+
isolated git worktree rooted at
|
|
5
|
+
`~/.okstra/worktrees/<project_id>/<task_group>/<task_id>/`. The same
|
|
6
|
+
worktree is reused across all phases of one task-key (requirements-
|
|
7
|
+
discovery → error-analysis → implementation-option-selection →
|
|
8
|
+
implementation-planning → implementation), so phase N picks up exactly
|
|
9
|
+
the working-tree state phase N-1 left behind.
|
|
10
|
+
|
|
11
|
+
A global registry (`worktree_registry.py`) maps task-keys to the
|
|
12
|
+
on-disk path + branch and serialises reservations, so two concurrent
|
|
13
|
+
okstra runs cannot collide on the same path or branch name.
|
|
14
|
+
|
|
15
|
+
Pre-conditions handled here:
|
|
16
|
+
- Skip when `project_root` is not a git repo (degrade gracefully).
|
|
17
|
+
- Skip when `project_root` itself is already a non-main worktree
|
|
18
|
+
(caller's tree is already an isolated workspace; reuse it).
|
|
19
|
+
- Re-entry of the same task-key returns the existing worktree.
|
|
20
|
+
- Branch / path collisions across task-keys raise PrepareError-like
|
|
21
|
+
RuntimeError.
|
|
22
|
+
|
|
23
|
+
Side effects:
|
|
24
|
+
- `git worktree add -b <branch> <path> <base_ref>` invoked in the
|
|
25
|
+
main worktree of `project_root` (NOT `project_root` itself when it
|
|
26
|
+
is also a worktree — base must be the main checkout).
|
|
27
|
+
- Per-task sync dirs (.project-docs, .scratch, graphify-out by
|
|
28
|
+
default) symlinked from the **main worktree** into the new
|
|
29
|
+
worktree so every task sees the same shared state, irrespective of
|
|
30
|
+
which worktree the caller invoked okstra from.
|
|
31
|
+
- The function does NOT chdir.
|
|
32
|
+
|
|
33
|
+
구현은 여섯 층으로 나뉜다. 바닥의 세 층 — `naming`(문자열 계산),
|
|
34
|
+
`sync_config`(무엇을 옮길지), `git_ops`(git 사실) — 은 서로를 모른다. 그 위에
|
|
35
|
+
`cleanliness`(무엇이 더러운가) · `linking`(실제로 링크 걸기) ·
|
|
36
|
+
`decisions`(무엇이 일어날지 미리 답하기)가 있고, 부수 효과를 내는 것은
|
|
37
|
+
`provision` 하나뿐이다. 이 파일은 밖으로 나가는 이름만 모아 둔다.
|
|
38
|
+
"""
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
from .cleanliness import (
|
|
42
|
+
dirty_entries_excluding_okstra,
|
|
43
|
+
is_dirty_excluding_okstra,
|
|
44
|
+
nested_worktree_excludes,
|
|
45
|
+
okstra_clean_gate_excludes,
|
|
46
|
+
)
|
|
47
|
+
from .decisions import (
|
|
48
|
+
StageWorktreeDecision,
|
|
49
|
+
WorktreeDecision,
|
|
50
|
+
preview_worktree_decision,
|
|
51
|
+
resolve_stage_worktree_decision,
|
|
52
|
+
)
|
|
53
|
+
from .git_ops import (
|
|
54
|
+
MergeError,
|
|
55
|
+
_git,
|
|
56
|
+
_resolve_commit_sha,
|
|
57
|
+
is_ancestor,
|
|
58
|
+
is_git_work_tree,
|
|
59
|
+
main_worktree_path,
|
|
60
|
+
merge_branch,
|
|
61
|
+
remove_worktree_force,
|
|
62
|
+
)
|
|
63
|
+
from .naming import compute_branch_name, compute_worktree_path
|
|
64
|
+
from .provision import (
|
|
65
|
+
WorktreeProvision,
|
|
66
|
+
provision_stage_worktree,
|
|
67
|
+
provision_task_worktree,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
__all__ = [
|
|
71
|
+
"MergeError",
|
|
72
|
+
"StageWorktreeDecision",
|
|
73
|
+
"WorktreeDecision",
|
|
74
|
+
"WorktreeProvision",
|
|
75
|
+
"_git",
|
|
76
|
+
"_resolve_commit_sha",
|
|
77
|
+
"compute_branch_name",
|
|
78
|
+
"compute_worktree_path",
|
|
79
|
+
"dirty_entries_excluding_okstra",
|
|
80
|
+
"is_ancestor",
|
|
81
|
+
"is_dirty_excluding_okstra",
|
|
82
|
+
"is_git_work_tree",
|
|
83
|
+
"main_worktree_path",
|
|
84
|
+
"merge_branch",
|
|
85
|
+
"nested_worktree_excludes",
|
|
86
|
+
"okstra_clean_gate_excludes",
|
|
87
|
+
"preview_worktree_decision",
|
|
88
|
+
"provision_stage_worktree",
|
|
89
|
+
"provision_task_worktree",
|
|
90
|
+
"remove_worktree_force",
|
|
91
|
+
"resolve_stage_worktree_decision",
|
|
92
|
+
]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""okstra 기준으로 worktree 가 깨끗한지 판정한다.
|
|
2
|
+
|
|
3
|
+
`git status` 가 보는 더러움과 okstra 가 신경 쓰는 더러움은 다르다. okstra 는
|
|
4
|
+
자기 산출물(`.okstra/`), 자기가 걸어 둔 sync 심링크, 자기가 만든 중첩 worktree
|
|
5
|
+
를 제외하고 남는 변경만 사용자의 것으로 본다. 그 제외 목록을 만드는 곳이
|
|
6
|
+
여기이고, 그래서 `sync_config`(무엇을 걸었나)와 `git_ops`(지금 무엇이 바뀌었나)
|
|
7
|
+
양쪽을 쓴다.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from .git_ops import _git
|
|
15
|
+
from .sync_config import (
|
|
16
|
+
_resolve_snapshot_files,
|
|
17
|
+
_resolve_sync_dirs,
|
|
18
|
+
_resolve_sync_files,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def okstra_clean_gate_excludes(project_root: Optional[Path] = None) -> tuple[str, ...]:
|
|
23
|
+
"""Project-relative paths okstra owns and source clean gates should ignore."""
|
|
24
|
+
out: list[str] = []
|
|
25
|
+
seen: set[str] = set()
|
|
26
|
+
for rel in (
|
|
27
|
+
".okstra",
|
|
28
|
+
*_resolve_sync_dirs(project_root),
|
|
29
|
+
*_resolve_sync_files(project_root),
|
|
30
|
+
*_resolve_snapshot_files(project_root),
|
|
31
|
+
):
|
|
32
|
+
cleaned = rel.strip().removeprefix("./").rstrip("/")
|
|
33
|
+
if not cleaned or cleaned == "." or cleaned in seen:
|
|
34
|
+
continue
|
|
35
|
+
seen.add(cleaned)
|
|
36
|
+
out.append(cleaned)
|
|
37
|
+
return tuple(out)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def nested_worktree_excludes(worktree_path) -> tuple[str, ...]:
|
|
41
|
+
"""Relative paths of git worktrees nested under `worktree_path`.
|
|
42
|
+
|
|
43
|
+
implementation stage worktrees live inside the task worktree
|
|
44
|
+
(`<task-id>/stage-<N>/`), so the parent's `git status --short` reports
|
|
45
|
+
each as an untracked `?? stage-N/`. They are git-registered worktrees,
|
|
46
|
+
not user source changes, so clean gates must ignore them. Derived from
|
|
47
|
+
`git worktree list` rather than a hardcoded pattern so it tracks exactly
|
|
48
|
+
what git registered.
|
|
49
|
+
"""
|
|
50
|
+
root = Path(worktree_path).resolve()
|
|
51
|
+
r = _git(root, "worktree", "list", "--porcelain")
|
|
52
|
+
if r.returncode != 0:
|
|
53
|
+
return ()
|
|
54
|
+
out: list[str] = []
|
|
55
|
+
for line in r.stdout.splitlines():
|
|
56
|
+
if not line.startswith("worktree "):
|
|
57
|
+
continue
|
|
58
|
+
wt = Path(line[len("worktree "):].strip()).resolve()
|
|
59
|
+
if wt == root:
|
|
60
|
+
continue
|
|
61
|
+
try:
|
|
62
|
+
rel = wt.relative_to(root)
|
|
63
|
+
except ValueError:
|
|
64
|
+
continue
|
|
65
|
+
out.append(str(rel))
|
|
66
|
+
return tuple(out)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def dirty_entries_excluding_okstra(cwd) -> list[str]:
|
|
70
|
+
"""`git status --short` rows for changes outside okstra-owned paths.
|
|
71
|
+
|
|
72
|
+
okstra-owned = `okstra_clean_gate_excludes` (e.g. `.okstra`, synced dirs)
|
|
73
|
+
plus `nested_worktree_excludes` (stage worktrees nested under `cwd`).
|
|
74
|
+
|
|
75
|
+
This is the clean-worktree question every okstra gate asks, and the one a
|
|
76
|
+
plan step must ask through `okstra worktree-status` rather than through a
|
|
77
|
+
bare `git status --porcelain`: okstra provisions `.okstra` plus the synced
|
|
78
|
+
entries into every task worktree, so a bare status is never empty there and
|
|
79
|
+
an assertion built on it fails on okstra's own scaffolding.
|
|
80
|
+
"""
|
|
81
|
+
owned = (*okstra_clean_gate_excludes(Path(cwd)), *nested_worktree_excludes(cwd))
|
|
82
|
+
excludes = [f":(exclude){p}" for p in owned]
|
|
83
|
+
out = _git(Path(cwd), "status", "--short", "--", ".", *excludes).stdout
|
|
84
|
+
return [line for line in out.splitlines() if line.strip()]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def is_dirty_excluding_okstra(cwd) -> bool:
|
|
88
|
+
"""True iff the worktree has changes outside okstra-owned paths."""
|
|
89
|
+
return bool(dirty_entries_excluding_okstra(cwd))
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""provision 하기 전에 무엇이 일어날지 답한다.
|
|
2
|
+
|
|
3
|
+
위저드는 사용자에게 경로와 브랜치를 보여 준 뒤 확인을 받는다. 그 시점에 이미
|
|
4
|
+
worktree 를 만들어 버리면 안 되므로, 판단(재사용인가 신규인가, 어느 경로인가)만
|
|
5
|
+
떼어 여기에 둔다. 레지스트리를 읽기는 하지만 예약하지는 않는다 — 예약은
|
|
6
|
+
`provision` 의 몫이다.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from .. import worktree_registry
|
|
14
|
+
from .git_ops import _is_inside_non_main_worktree, is_git_work_tree
|
|
15
|
+
from .naming import _safe_segment, compute_branch_name, compute_worktree_path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class WorktreeDecision:
|
|
20
|
+
"""Side-effect-free preview of what `provision_task_worktree` would do.
|
|
21
|
+
|
|
22
|
+
status:
|
|
23
|
+
- "new": no active registry entry; a fresh worktree would be created
|
|
24
|
+
- "reused": registry already has this task-key; existing path/branch returned
|
|
25
|
+
- "skipped-in-worktree": project_root is itself a non-main worktree
|
|
26
|
+
- "skipped-not-git": project_root has no .git
|
|
27
|
+
"""
|
|
28
|
+
status: str
|
|
29
|
+
path: str # worktree path (new: prospective; reuse: existing; skip: project_root)
|
|
30
|
+
branch: str = "" # new: prospective branch; reused: existing branch
|
|
31
|
+
base_ref: str = "" # new: requested base_ref; reused: existing base
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def preview_worktree_decision(
|
|
35
|
+
*,
|
|
36
|
+
project_root,
|
|
37
|
+
project_id: str,
|
|
38
|
+
task_group_segment: str,
|
|
39
|
+
task_id_segment: str,
|
|
40
|
+
work_category: str,
|
|
41
|
+
base_ref: str = "",
|
|
42
|
+
) -> "WorktreeDecision":
|
|
43
|
+
"""Side-effect-free: what provision_task_worktree WOULD do, without touching disk.
|
|
44
|
+
|
|
45
|
+
Mirrors provision's decision branches exactly; reuses the same read-only
|
|
46
|
+
helpers so preview never diverges from the actual provisioning result.
|
|
47
|
+
"""
|
|
48
|
+
project_root = Path(project_root)
|
|
49
|
+
if not is_git_work_tree(project_root):
|
|
50
|
+
return WorktreeDecision(status="skipped-not-git", path=str(project_root))
|
|
51
|
+
if _is_inside_non_main_worktree(project_root):
|
|
52
|
+
return WorktreeDecision(status="skipped-in-worktree", path=str(project_root))
|
|
53
|
+
safe_project = _safe_segment(project_id)
|
|
54
|
+
safe_group = _safe_segment(task_group_segment)
|
|
55
|
+
safe_task = _safe_segment(task_id_segment)
|
|
56
|
+
existing = worktree_registry.lookup(safe_project, safe_group, safe_task)
|
|
57
|
+
if existing is not None and existing.status == "active":
|
|
58
|
+
return WorktreeDecision(
|
|
59
|
+
status="reused", path=existing.worktree_path,
|
|
60
|
+
branch=existing.branch, base_ref=existing.base_ref,
|
|
61
|
+
)
|
|
62
|
+
return WorktreeDecision(
|
|
63
|
+
status="new",
|
|
64
|
+
path=str(compute_worktree_path(
|
|
65
|
+
project_id=safe_project, task_group_segment=safe_group,
|
|
66
|
+
task_id_segment=safe_task)),
|
|
67
|
+
branch=compute_branch_name(work_category=work_category, task_id_segment=safe_task),
|
|
68
|
+
base_ref=base_ref,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class StageWorktreeDecision:
|
|
74
|
+
"""Side-effect-free decision for one concrete implementation stage."""
|
|
75
|
+
|
|
76
|
+
status: str
|
|
77
|
+
path: str
|
|
78
|
+
branch: str = ""
|
|
79
|
+
base_ref: str = ""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def resolve_stage_worktree_decision(
|
|
83
|
+
*,
|
|
84
|
+
project_id: str,
|
|
85
|
+
task_group_segment: str,
|
|
86
|
+
task_id_segment: str,
|
|
87
|
+
work_category: str,
|
|
88
|
+
stage_number: int,
|
|
89
|
+
) -> StageWorktreeDecision:
|
|
90
|
+
"""Resolve whether one concrete stage worktree is new or reusable."""
|
|
91
|
+
safe_project = _safe_segment(project_id)
|
|
92
|
+
safe_group = _safe_segment(task_group_segment)
|
|
93
|
+
safe_task = _safe_segment(task_id_segment)
|
|
94
|
+
existing = worktree_registry.lookup(
|
|
95
|
+
safe_project, safe_group, safe_task, stage_number=stage_number)
|
|
96
|
+
if existing is not None and _stage_entry_is_reusable(existing):
|
|
97
|
+
return StageWorktreeDecision(
|
|
98
|
+
status="reused",
|
|
99
|
+
path=existing.worktree_path,
|
|
100
|
+
branch=existing.branch,
|
|
101
|
+
base_ref=existing.base_ref,
|
|
102
|
+
)
|
|
103
|
+
return StageWorktreeDecision(
|
|
104
|
+
status="new",
|
|
105
|
+
path=str(compute_worktree_path(
|
|
106
|
+
project_id=safe_project, task_group_segment=safe_group,
|
|
107
|
+
task_id_segment=safe_task, stage_number=stage_number)),
|
|
108
|
+
branch=compute_branch_name(
|
|
109
|
+
work_category=work_category, task_id_segment=safe_task,
|
|
110
|
+
stage_number=stage_number),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _stage_entry_is_reusable(entry: worktree_registry.WorktreeEntry) -> bool:
|
|
115
|
+
"""Whether a registered stage worktree can be entered by this run.
|
|
116
|
+
|
|
117
|
+
`active` is the live-run case. `released` with the directory still on disk is
|
|
118
|
+
the fix-run case: a stage whose verifier returned FAIL records a `failed`
|
|
119
|
+
consumers row, which frees the occupancy but deliberately keeps the worktree
|
|
120
|
+
and its branch as the reviewable stack. Re-entry MUST reuse that tree —
|
|
121
|
+
provisioning anew refuses on the existing path and branch. After whole-task
|
|
122
|
+
final-verification removes the directory the entry stops being reusable, so
|
|
123
|
+
the stage provisions from scratch.
|
|
124
|
+
"""
|
|
125
|
+
if entry.status == "active":
|
|
126
|
+
return True
|
|
127
|
+
return entry.status == "released" and Path(entry.worktree_path).is_dir()
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""worktree 가 쓰는 git 명령 한 겹.
|
|
2
|
+
|
|
3
|
+
`_git` 은 `check=False` 라서 호출부가 returncode 를 보고 판단한다. 이 층은
|
|
4
|
+
okstra 의 규칙을 하나도 모른다 — 브랜치가 존재하는지, HEAD 가 무엇인지,
|
|
5
|
+
머지가 충돌했는지 같은 git 사실만 답한다. okstra 가 무엇을 무시하기로 했는지는
|
|
6
|
+
`cleanliness` 가, 무엇을 옮기기로 했는지는 `sync_config` 가 안다.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import subprocess
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _git(cwd: Path, *args: str) -> subprocess.CompletedProcess:
|
|
15
|
+
return subprocess.run(
|
|
16
|
+
["git", "-C", str(cwd), *args],
|
|
17
|
+
capture_output=True, text=True, check=False,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _is_inside_non_main_worktree(project_root: Path) -> bool:
|
|
22
|
+
"""True iff project_root is inside a git worktree that is NOT the
|
|
23
|
+
repository's main checkout. Detection rule: `--git-dir` (per-worktree
|
|
24
|
+
.git pointer) differs from `--git-common-dir` (shared object store).
|
|
25
|
+
"""
|
|
26
|
+
common = _git(project_root, "rev-parse", "--git-common-dir")
|
|
27
|
+
per_tree = _git(project_root, "rev-parse", "--git-dir")
|
|
28
|
+
if common.returncode != 0 or per_tree.returncode != 0:
|
|
29
|
+
return False
|
|
30
|
+
common_abs = (project_root / common.stdout.strip()).resolve()
|
|
31
|
+
per_tree_abs = (project_root / per_tree.stdout.strip()).resolve()
|
|
32
|
+
return common_abs != per_tree_abs
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def is_git_work_tree(project_root: Path) -> bool:
|
|
36
|
+
"""project_root 가 git work tree 내부인지 판정하는 public git-introspection seam.
|
|
37
|
+
|
|
38
|
+
git 미설치(FileNotFoundError) 를 포함한 모든 실패는 False — 호출자(migrate
|
|
39
|
+
등)가 non-git 레이아웃으로 안전하게 폴백할 수 있도록 한다. 과거 migrate.py
|
|
40
|
+
가 같은 판정을 자체 구현(`--show-toplevel`)했는데 이 seam 으로 통합한다."""
|
|
41
|
+
try:
|
|
42
|
+
res = _git(project_root, "rev-parse", "--is-inside-work-tree")
|
|
43
|
+
except (OSError, FileNotFoundError):
|
|
44
|
+
return False
|
|
45
|
+
return res.returncode == 0 and res.stdout.strip() == "true"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _branch_exists(project_root: Path, branch: str) -> bool:
|
|
49
|
+
res = _git(project_root, "rev-parse", "--verify", "--quiet", f"refs/heads/{branch}")
|
|
50
|
+
return res.returncode == 0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _branch_checkout_path(project_root: Path, branch: str) -> str:
|
|
54
|
+
"""The worktree that currently has *branch* checked out, or "" when none does.
|
|
55
|
+
|
|
56
|
+
`git branch -D` refuses to delete a checked-out branch, so an existing-branch
|
|
57
|
+
error that only recommends `-D` is unfollowable whenever the branch is the
|
|
58
|
+
main checkout's HEAD. Callers use this to name the extra step instead.
|
|
59
|
+
"""
|
|
60
|
+
res = _git(project_root, "worktree", "list", "--porcelain")
|
|
61
|
+
if res.returncode != 0:
|
|
62
|
+
return ""
|
|
63
|
+
current = ""
|
|
64
|
+
for line in res.stdout.splitlines():
|
|
65
|
+
if line.startswith("worktree "):
|
|
66
|
+
current = line[len("worktree "):].strip()
|
|
67
|
+
elif line.strip() == f"branch refs/heads/{branch}":
|
|
68
|
+
return current
|
|
69
|
+
return ""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _branch_exists_message(project_root: Path, branch: str, kind: str) -> str:
|
|
73
|
+
"""Existing-branch error text whose remedy is actually runnable."""
|
|
74
|
+
checkout = _branch_checkout_path(project_root, branch)
|
|
75
|
+
if checkout:
|
|
76
|
+
return (
|
|
77
|
+
f"{kind} worktree branch already exists: {branch}, and it is checked "
|
|
78
|
+
f"out at {checkout}. `git branch -D {branch}` will refuse while it is "
|
|
79
|
+
f"checked out — switch that checkout to another branch first "
|
|
80
|
+
f"(`git -C {checkout} switch <other-branch>`), then delete it, or "
|
|
81
|
+
"choose a different work-category before retrying."
|
|
82
|
+
)
|
|
83
|
+
return (
|
|
84
|
+
f"{kind} worktree branch already exists: {branch}. "
|
|
85
|
+
f"Delete it (`git branch -D {branch}`) or choose a different "
|
|
86
|
+
"work-category before retrying."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _head_sha(cwd: Path) -> str:
|
|
91
|
+
res = _git(cwd, "rev-parse", "HEAD")
|
|
92
|
+
if res.returncode != 0:
|
|
93
|
+
return ""
|
|
94
|
+
return res.stdout.strip()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _resolve_commit_sha(cwd: Path, ref: str) -> str:
|
|
98
|
+
"""Resolve a user-supplied ref (branch, tag, short/long SHA) to a full
|
|
99
|
+
commit SHA in `cwd`. Returns empty string when the ref is not resolvable
|
|
100
|
+
so the caller can raise a contextual error.
|
|
101
|
+
"""
|
|
102
|
+
res = _git(cwd, "rev-parse", "--verify", "--quiet", f"{ref}^{{commit}}")
|
|
103
|
+
if res.returncode != 0:
|
|
104
|
+
return ""
|
|
105
|
+
return res.stdout.strip()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def main_worktree_path(project_root: Path) -> Path:
|
|
109
|
+
"""Locate the repository's MAIN worktree (the original checkout).
|
|
110
|
+
|
|
111
|
+
`git worktree list --porcelain` lists worktrees in a stable order
|
|
112
|
+
where the first `worktree <path>` block is the main checkout.
|
|
113
|
+
Falls back to `project_root` if parsing fails — caller still gets
|
|
114
|
+
a working path, sync-dir links just point at the caller's tree
|
|
115
|
+
(the prior behaviour).
|
|
116
|
+
"""
|
|
117
|
+
res = _git(project_root, "worktree", "list", "--porcelain")
|
|
118
|
+
if res.returncode != 0:
|
|
119
|
+
return project_root
|
|
120
|
+
for line in res.stdout.splitlines():
|
|
121
|
+
if line.startswith("worktree "):
|
|
122
|
+
return Path(line[len("worktree "):].strip())
|
|
123
|
+
return project_root
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def is_ancestor(cwd, commit: str, head: str) -> bool:
|
|
127
|
+
"""True iff `commit` is an ancestor of `head` (both non-empty)."""
|
|
128
|
+
if not commit or not head:
|
|
129
|
+
return False
|
|
130
|
+
return _git(Path(cwd), "merge-base", "--is-ancestor", commit, head).returncode == 0
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class MergeError(RuntimeError):
|
|
134
|
+
"""`git merge` 가 내용 충돌이 아닌 사유로 실패했을 때(추적되지 않은 파일
|
|
135
|
+
덮어쓰기, 진행 중인 머지 잔존, 없는 브랜치 등) 던진다. 충돌 파일이 0개인
|
|
136
|
+
실패를 '충돌'로 둔갑시키지 않고 git stderr 를 그대로 노출해 호출자가 올바른
|
|
137
|
+
복구 경로로 안내하도록 한다."""
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def merge_branch(cwd, branch: str, *, no_ff: bool) -> list[str] | None:
|
|
141
|
+
"""`branch` 를 cwd 의 현재 HEAD 에 머지한다. 성공이면 None, 내용 충돌이면
|
|
142
|
+
abort 후 충돌 파일 목록을 반환한다(최소 1개). 충돌이 아닌 머지 실패는
|
|
143
|
+
MergeError 로 던진다 — 충돌 0개를 충돌로 보고하지 않기 위함. 호출자가 충돌
|
|
144
|
+
목록을 HandoffConflict/IntegrateError 등 적절한 예외로 변환한다 — 머지·
|
|
145
|
+
충돌감지·abort 시퀀스의 단일 지점."""
|
|
146
|
+
args = ["merge", "--no-ff", "--no-edit", branch] if no_ff \
|
|
147
|
+
else ["merge", "--no-edit", branch]
|
|
148
|
+
res = _git(Path(cwd), *args)
|
|
149
|
+
if res.returncode == 0:
|
|
150
|
+
return None
|
|
151
|
+
conflicts = _git(Path(cwd), "diff", "--name-only",
|
|
152
|
+
"--diff-filter=U").stdout.split()
|
|
153
|
+
_git(Path(cwd), "merge", "--abort")
|
|
154
|
+
if not conflicts:
|
|
155
|
+
raise MergeError((res.stderr or res.stdout).strip()
|
|
156
|
+
or f"git merge {branch} failed (exit={res.returncode})")
|
|
157
|
+
return conflicts
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def remove_worktree_force(cwd, worktree_path) -> subprocess.CompletedProcess:
|
|
161
|
+
"""워크트리를 강제 제거한다. provision_*_worktree 가 워크트리에 .okstra·
|
|
162
|
+
synced 디렉터리/파일 symlink(untracked) 를 깔기 때문에 plain remove 는 그
|
|
163
|
+
잔여물 때문에 git 이 거부한다 — dirty 게이트로 실사용자 변경을 막은 뒤
|
|
164
|
+
--force 로 okstra 잔여물만 정리하는 단일 지점."""
|
|
165
|
+
return _git(Path(cwd), "worktree", "remove", "--force", str(worktree_path))
|