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
|
@@ -7,60 +7,10 @@ from pathlib import Path
|
|
|
7
7
|
from typing import List, Optional
|
|
8
8
|
|
|
9
9
|
from .paths import resolve_under_root
|
|
10
|
-
from .json_boundary import JsonBoundaryError, load_owned_object
|
|
11
10
|
from .reconcile import _parse_iso
|
|
12
11
|
from .run_index_row import read_run_index
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
def list_projects(home: Path, *, only_active: bool = False,
|
|
16
|
-
sort_by: str = "recent") -> List[dict]:
|
|
17
|
-
"""projects/<id>/meta.json 을 모두 읽어 정렬된 목록을 반환."""
|
|
18
|
-
base = home / "projects"
|
|
19
|
-
if not base.is_dir():
|
|
20
|
-
return []
|
|
21
|
-
items: List[dict] = []
|
|
22
|
-
for child in base.iterdir():
|
|
23
|
-
meta_file = child / "meta.json"
|
|
24
|
-
if not meta_file.is_file():
|
|
25
|
-
continue
|
|
26
|
-
try:
|
|
27
|
-
meta = load_owned_object(meta_file, artifact="project metadata")
|
|
28
|
-
except JsonBoundaryError:
|
|
29
|
-
# 동시 쓰기/이전 크래시로 깨진 meta.json 하나가 전체 listing 을
|
|
30
|
-
# 죽이지 않도록 해당 프로젝트만 건너뛴다(다른 JSON 리더와 동일 정책).
|
|
31
|
-
continue
|
|
32
|
-
# projectRoot 가 누락/빈 문자열이면 Path("").is_dir() 이 cwd 를 가리켜
|
|
33
|
-
# True 가 되므로(missing 으로 잡히지 않음), 빈 값은 명시적으로 missing
|
|
34
|
-
# 처리한다.
|
|
35
|
-
proj_root = meta.get("projectRoot") or ""
|
|
36
|
-
meta["missing"] = (not proj_root) or (not Path(proj_root).is_dir())
|
|
37
|
-
if only_active and int(meta.get("activeCount", 0)) <= 0:
|
|
38
|
-
continue
|
|
39
|
-
items.append(meta)
|
|
40
|
-
if sort_by == "name":
|
|
41
|
-
items.sort(key=lambda m: m.get("projectId", ""))
|
|
42
|
-
elif sort_by == "count":
|
|
43
|
-
items.sort(key=lambda m: int(m.get("runCount", 0)), reverse=True)
|
|
44
|
-
else:
|
|
45
|
-
items.sort(key=lambda m: m.get("lastRunAt", ""), reverse=True)
|
|
46
|
-
return items
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def format_projects_table(items: List[dict]) -> str:
|
|
50
|
-
"""projects 테이블 텍스트 포맷팅."""
|
|
51
|
-
rows = [("PROJECT-ID", "RUNS", "ACTIVE", "LAST-RUN", "ROOT")]
|
|
52
|
-
for it in items:
|
|
53
|
-
root = it.get("projectRoot", "") + (" (MISSING)" if it.get("missing") else "")
|
|
54
|
-
rows.append((it.get("projectId", ""), str(it.get("runCount", 0)),
|
|
55
|
-
str(it.get("activeCount", 0)), it.get("lastRunAt", ""),
|
|
56
|
-
root))
|
|
57
|
-
widths = [max(len(r[i]) for r in rows) for i in range(5)]
|
|
58
|
-
lines = []
|
|
59
|
-
for r in rows:
|
|
60
|
-
lines.append(" ".join(c.ljust(widths[i]) for i, c in enumerate(r)))
|
|
61
|
-
return "\n".join(lines) + "\n"
|
|
62
|
-
|
|
63
|
-
|
|
64
14
|
def _parse_since(value: str) -> Optional[datetime]:
|
|
65
15
|
"""--since 파싱. 빈 문자열은 None(필터 없음). 형식 위반은 ValueError 로
|
|
66
16
|
fail-fast 한다. 이전엔 잘못된 값(`7days`)이 silently None 으로 강등되어
|
|
@@ -133,47 +83,6 @@ def list_runs(home: Path, *, project: str = "all", task_group: str = "all",
|
|
|
133
83
|
return out
|
|
134
84
|
|
|
135
85
|
|
|
136
|
-
def format_runs_table(rows: List[dict]) -> str:
|
|
137
|
-
header = ("RUN-ID", "STATUS", "STARTED", "TASK-TYPE")
|
|
138
|
-
body = [(r.get("runId", ""), r.get("status", ""),
|
|
139
|
-
r.get("startedAt", ""), r.get("taskType", "")) for r in rows]
|
|
140
|
-
table = [header] + body
|
|
141
|
-
widths = [max(len(t[i]) for t in table) for i in range(4)]
|
|
142
|
-
lines = []
|
|
143
|
-
for t in table:
|
|
144
|
-
lines.append(" ".join(c.ljust(widths[i]) for i, c in enumerate(t)))
|
|
145
|
-
return "\n".join(lines) + "\n"
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def find_row_by_run_id(home: Path, run_id: str) -> Optional[dict]:
|
|
149
|
-
"""active+recent+archive 를 모두 뒤져 runId 가 일치하는 최신 row 반환."""
|
|
150
|
-
rows = list_runs(home, include_archive=True)
|
|
151
|
-
for r in rows:
|
|
152
|
-
if r.get("runId") == run_id:
|
|
153
|
-
return r
|
|
154
|
-
return None
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def format_show(row: dict) -> str:
|
|
158
|
-
inv = row.get("invocationFile", "")
|
|
159
|
-
return (
|
|
160
|
-
f"runId : {row.get('runId')}\n"
|
|
161
|
-
f"projectId : {row.get('projectId')}\n"
|
|
162
|
-
f"projectRoot : {row.get('projectRoot')}\n"
|
|
163
|
-
f"taskKey : {row.get('taskGroup')}/{row.get('taskId')}\n"
|
|
164
|
-
f"taskType : {row.get('taskType')}\n"
|
|
165
|
-
f"status : {row.get('status')}\n"
|
|
166
|
-
f"startedAt : {row.get('startedAt')}\n"
|
|
167
|
-
f"finishedAt : {row.get('finishedAt')}\n"
|
|
168
|
-
f"workers : {','.join(row.get('workers', []))}\n"
|
|
169
|
-
f"leadModel : {row.get('leadModel')}\n"
|
|
170
|
-
f"validation : {row.get('validation')}\n"
|
|
171
|
-
f"runDirRel : {row.get('runDirRel')}\n"
|
|
172
|
-
f"finalReportRecordRel : {row.get('finalReportRecordRel') or row.get('finalReportRel')}\n"
|
|
173
|
-
f"invocationFile : {inv}\n"
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
|
|
177
86
|
def absolute_final_report_path(row: dict) -> Optional[Path]:
|
|
178
87
|
from .final_report_paths import index_report_record_rel
|
|
179
88
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""중앙 락과 task-
|
|
1
|
+
"""중앙 락과 task-key 단위 mutex. 다른 okstra_ctl 모듈에 의존하지 않는다."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import contextlib
|
|
@@ -9,9 +9,10 @@ from .ids import _escape_segment_for_join, _safe_fs_segment
|
|
|
9
9
|
|
|
10
10
|
@contextlib.contextmanager
|
|
11
11
|
def central_lock(home):
|
|
12
|
-
"""~/.okstra/.lock 위에 fcntl LOCK_EX.
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
"""~/.okstra/.lock 위에 fcntl LOCK_EX.
|
|
13
|
+
|
|
14
|
+
중앙 인덱스(active.jsonl·recent.jsonl)를 쓰는 모든 경로가 이 한 파일을
|
|
15
|
+
거치므로 record_start·reconcile·reserve 가 서로 직렬화된다.
|
|
15
16
|
"""
|
|
16
17
|
from pathlib import Path as _Path
|
|
17
18
|
home_p = _Path(home)
|
|
@@ -57,18 +58,3 @@ def worktree_provision_mutex(home, project_id: str, task_group: str,
|
|
|
57
58
|
yield
|
|
58
59
|
finally:
|
|
59
60
|
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def task_lock_filename(project_id: str, task_group: str, task_id: str,
|
|
63
|
-
task_type: str) -> str:
|
|
64
|
-
"""task-key + task-type 단위 mutex 파일명. ~/.okstra/.locks/ 아래에 위치한다.
|
|
65
|
-
okstra 의 RUN_DIR 가 task-type 별로 분리되므로 mutex 도 같은 입자에 둔다.
|
|
66
|
-
각 세그먼트는 먼저 fs-safe 슬러그로 정규화해 `/` 나 `..` 가 .locks 디렉터리
|
|
67
|
-
밖으로 mutex 를 escape 시키는 것을 막고, 이어서 `-` 를 `--` 로 escape 한
|
|
68
|
-
뒤 `-` 로 조인해 슬래시 경계를 보존한다(('p','feature-8','email','x') 와
|
|
69
|
-
('p','feature','8-email','x') 가 같은 파일명으로 매핑되어 mutex 가 공유
|
|
70
|
-
되는 문제 방지).
|
|
71
|
-
"""
|
|
72
|
-
parts = [_escape_segment_for_join(_safe_fs_segment(s))
|
|
73
|
-
for s in (project_id, task_group, task_id, task_type)]
|
|
74
|
-
return "-".join(parts) + ".lock"
|
|
@@ -140,8 +140,22 @@ def render_result_text(result: dict) -> str:
|
|
|
140
140
|
return "".join(parts)
|
|
141
141
|
|
|
142
142
|
|
|
143
|
+
_CLI_EPILOG = r"""Usage:
|
|
144
|
+
okstra log-report [--project-root <dir>] [--cwd <dir>] [--top <N>] [--json]
|
|
145
|
+
|
|
146
|
+
Output: JSON { ok, projectRoot, logsRoot, topLargest[], perTask[], totals }.
|
|
147
|
+
Scans <project-root>/.okstra/tasks/**/runs/*/prompts/*.log. Each entry reports
|
|
148
|
+
the wrapper transcript as transcriptBytes and its sibling prompt Markdown as
|
|
149
|
+
promptBytes; sizeBytes remains the transcript-size compatibility field. Sizes
|
|
150
|
+
are raw bytes and mtimes are epoch seconds — the caller formats KB/MB and ages.
|
|
151
|
+
Read-only: it never deletes logs (cleanup commands stay in the skill).
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
|
|
143
155
|
def main(argv: list[str] | None = None) -> int:
|
|
144
156
|
parser = argparse.ArgumentParser(
|
|
157
|
+
epilog=_CLI_EPILOG,
|
|
158
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
145
159
|
prog="okstra log-report",
|
|
146
160
|
description="Inventory wrapper sidecar logs by size and task (read-only).")
|
|
147
161
|
parser.add_argument("--project-root", default="")
|
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
5
5
|
import json
|
|
6
|
-
import os
|
|
7
6
|
import sys
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
|
|
@@ -146,8 +145,29 @@ def _parse_child_specs(values: list[str], task_group: str, default_task_id: str)
|
|
|
146
145
|
return specs
|
|
147
146
|
|
|
148
147
|
|
|
148
|
+
_CLI_EPILOG = r"""Usage:
|
|
149
|
+
okstra manager init --manager-id <id>
|
|
150
|
+
okstra manager discover-projects
|
|
151
|
+
okstra manager new project --manager-id <id> --project-id <id> --project-root <path> [--role <role>] [--tag <tag>]
|
|
152
|
+
okstra manager new task-group --manager-id <id> --task-group <task-group>
|
|
153
|
+
okstra manager new task --manager-id <id> --task-group <task-group> --task-id <task-id> [--task <project-id:task-group:task-id>]
|
|
154
|
+
okstra manager task status --manager-id <id> --task-group <task-group> --task-id <task-id>
|
|
155
|
+
okstra manager task sync --manager-id <id> --task-group <task-group> --task-id <task-id>
|
|
156
|
+
okstra manager task assign --manager-id <id> --task-group <task-group> --task-id <task-id> --project-id <project-id>
|
|
157
|
+
okstra manager task note --manager-id <id> --task-group <task-group> --task-id <task-id> --scope <shared|project> --body <text>
|
|
158
|
+
okstra manager task run --manager-id <id> --project-id <project-id> --task-group <task-group> --task-id <task-id>
|
|
159
|
+
|
|
160
|
+
--workspace-root is owned by this command.
|
|
161
|
+
"""
|
|
162
|
+
_CLI_DESCRIPTION = "Manage cross-project okstra tasks."
|
|
163
|
+
|
|
164
|
+
|
|
149
165
|
def _parser() -> argparse.ArgumentParser:
|
|
150
|
-
parser = argparse.ArgumentParser(
|
|
166
|
+
parser = argparse.ArgumentParser(
|
|
167
|
+
description=_CLI_DESCRIPTION,
|
|
168
|
+
epilog=_CLI_EPILOG,
|
|
169
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
170
|
+
prog="okstra manager")
|
|
151
171
|
parser.add_argument("--json", action="store_true")
|
|
152
172
|
parser.add_argument("--workspace-root", default="")
|
|
153
173
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -299,7 +319,6 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
299
319
|
args.task_id,
|
|
300
320
|
args.project_id,
|
|
301
321
|
child_task_id=args.child_task_id,
|
|
302
|
-
env=os.environ,
|
|
303
322
|
),
|
|
304
323
|
args.json,
|
|
305
324
|
)
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Mapping
|
|
6
5
|
|
|
7
6
|
from okstra_ctl.jsonl import append_jsonl, read_jsonl
|
|
8
7
|
|
|
@@ -19,13 +18,12 @@ from .manager_store import ManagerError, _now_iso, _read_json, _read_json_defaul
|
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
WORKER_DISPATCH_BACKEND = "subagent"
|
|
21
|
+
# child lead 는 언제나 subagent 로 뜬다. 예전에는 `$TMUX` 가 잡히면
|
|
22
|
+
# `tmux-child-lead` 를 골랐지만 tmux 백엔드가 사라져 분기가 남지 않았다.
|
|
23
|
+
CHILD_LEAD_BACKEND = "subagent-child-lead"
|
|
22
24
|
LAUNCH_STATUS_PREPARED = "prepared"
|
|
23
25
|
|
|
24
26
|
|
|
25
|
-
def select_child_lead_backend(env: Mapping[str, str]) -> str:
|
|
26
|
-
return "tmux-child-lead" if str(env.get("TMUX") or "").strip() else "subagent-child-lead"
|
|
27
|
-
|
|
28
|
-
|
|
29
27
|
def _find_project(projects: dict, project_id: str) -> dict:
|
|
30
28
|
for project in projects.get("projects", []):
|
|
31
29
|
if isinstance(project, dict) and project.get("projectId") == project_id:
|
|
@@ -189,7 +187,6 @@ def build_launch_packet(
|
|
|
189
187
|
project_id: str,
|
|
190
188
|
*,
|
|
191
189
|
child_task_id: str | None = None,
|
|
192
|
-
env: Mapping[str, str] | None = None,
|
|
193
190
|
now: str | None = None,
|
|
194
191
|
) -> dict:
|
|
195
192
|
manifest = _read_json(task_manifest_path(home, manager_id, task_group, task_id))
|
|
@@ -204,7 +201,6 @@ def build_launch_packet(
|
|
|
204
201
|
target_task_key = _target_task_key(project_id, task_group, child_task_id_value)
|
|
205
202
|
_validate_child_task_key(child, target_task_key)
|
|
206
203
|
project_root = _validated_project_root(project)
|
|
207
|
-
backend = select_child_lead_backend(env or {})
|
|
208
204
|
created_at = now or _now_iso()
|
|
209
205
|
context_path = child_context_path(home, manager_id, task_group, task_id, project_id, child_task_id_value)
|
|
210
206
|
context_path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -224,7 +220,7 @@ def build_launch_packet(
|
|
|
224
220
|
"taskId": task_id,
|
|
225
221
|
"projectId": project_id,
|
|
226
222
|
"taskKey": target_task_key,
|
|
227
|
-
"backend":
|
|
223
|
+
"backend": CHILD_LEAD_BACKEND,
|
|
228
224
|
"workerDispatchBackend": WORKER_DISPATCH_BACKEND,
|
|
229
225
|
"projectRoot": project_root,
|
|
230
226
|
"contextPath": str(context_path),
|
|
@@ -11,7 +11,7 @@ import json
|
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from typing import Optional
|
|
13
13
|
|
|
14
|
-
from .json_boundary import load_owned_object
|
|
14
|
+
from .json_boundary import JsonBoundaryError, load_owned_object
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def build_analysis_material(brief_path: Path, directive: str = "") -> str:
|
|
@@ -43,7 +43,7 @@ def resolve_related_tasks(
|
|
|
43
43
|
existing = manifest.get("relatedTasks") or []
|
|
44
44
|
if not isinstance(existing, list):
|
|
45
45
|
existing = []
|
|
46
|
-
except
|
|
46
|
+
except JsonBoundaryError:
|
|
47
47
|
existing = []
|
|
48
48
|
provided = [v.strip() for v in (raw_related or "").split(",") if v.strip()]
|
|
49
49
|
seen: set[str] = set()
|
|
@@ -389,6 +389,24 @@ def _replace_in_project_rows(
|
|
|
389
389
|
return updated, changed
|
|
390
390
|
|
|
391
391
|
|
|
392
|
+
_CLI_EPILOG = r"""Dry-run by default:
|
|
393
|
+
prints the migration plan as JSON and exits without touching anything.
|
|
394
|
+
|
|
395
|
+
Usage:
|
|
396
|
+
okstra migrate [--apply] [--cwd <dir>] [--quiet]
|
|
397
|
+
|
|
398
|
+
Options:
|
|
399
|
+
--apply Execute the migration (git mv + .gitignore + okstra-home
|
|
400
|
+
registry row updates). Without it, preview only.
|
|
401
|
+
--cwd Project root to migrate. Default: current directory.
|
|
402
|
+
--quiet Single-line JSON output.
|
|
403
|
+
|
|
404
|
+
Exits 1 when the migration is refused (.okstra/ already exists, or no
|
|
405
|
+
legacy .project-docs/okstra/ found).
|
|
406
|
+
"""
|
|
407
|
+
_CLI_DESCRIPTION = "Move legacy .project-docs/okstra/ to .okstra/ (one-shot)."
|
|
408
|
+
|
|
409
|
+
|
|
392
410
|
def main(argv: Optional[list[str]] = None) -> int:
|
|
393
411
|
"""`python3 -m okstra_ctl.migrate [--apply] [--cwd <dir>] [--quiet]`.
|
|
394
412
|
|
|
@@ -399,7 +417,11 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|
|
399
417
|
import argparse
|
|
400
418
|
import sys
|
|
401
419
|
|
|
402
|
-
p = argparse.ArgumentParser(
|
|
420
|
+
p = argparse.ArgumentParser(
|
|
421
|
+
description=_CLI_DESCRIPTION,
|
|
422
|
+
epilog=_CLI_EPILOG,
|
|
423
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
424
|
+
prog="okstra migrate")
|
|
403
425
|
p.add_argument("--apply", action="store_true",
|
|
404
426
|
help="실제 실행 (기본: dry-run 미리보기)")
|
|
405
427
|
p.add_argument("--cwd", default=".",
|
|
@@ -142,7 +142,6 @@ def _catalog_row(model, adapter, role: str | None) -> dict[str, Any]:
|
|
|
142
142
|
}
|
|
143
143
|
if not model.selectable:
|
|
144
144
|
row["reason"] = "not selectable"
|
|
145
|
-
return row
|
|
146
145
|
if role != "leader":
|
|
147
146
|
return row
|
|
148
147
|
if model.provider_id != adapter.descriptor.native_provider_id:
|
|
@@ -165,9 +164,10 @@ def _catalog_row(model, adapter, role: str | None) -> dict[str, Any]:
|
|
|
165
164
|
# resolution actually does — it accepts the model and falls back to
|
|
166
165
|
# `cli-wrapper` — leaving no way to find out why a lead was not native.
|
|
167
166
|
row["leaderRunner"] = "cli-wrapper"
|
|
168
|
-
row["reason"] = (
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
row["reason"] = "; ".join(filter(None, (
|
|
168
|
+
row["reason"],
|
|
169
|
+
f"native-session unavailable ({exc}); the lead runs via cli-wrapper",
|
|
170
|
+
)))
|
|
171
171
|
else:
|
|
172
172
|
row["leaderRunner"] = "native-session"
|
|
173
173
|
return row
|
|
@@ -234,8 +234,22 @@ def _write_json_atomic(path: Path, payload: dict[str, Any]) -> None:
|
|
|
234
234
|
write_owned_object_atomic(path, payload, artifact="model configuration")
|
|
235
235
|
|
|
236
236
|
|
|
237
|
+
_CLI_EPILOG = r"""Usage:
|
|
238
|
+
okstra model list [--role <role>] [--host <host>] [--json]
|
|
239
|
+
okstra model default set <role> <modelRef[,modelRef...]> --scope project|global [--cwd <dir>]
|
|
240
|
+
okstra model default unset <role> --scope project|global [--cwd <dir>]
|
|
241
|
+
|
|
242
|
+
Listing and default writes never send an inference call.
|
|
243
|
+
"""
|
|
244
|
+
_CLI_DESCRIPTION = "List catalog models and manage role defaults."
|
|
245
|
+
|
|
246
|
+
|
|
237
247
|
def main(argv: list[str] | None = None) -> int:
|
|
238
|
-
parser = argparse.ArgumentParser(
|
|
248
|
+
parser = argparse.ArgumentParser(
|
|
249
|
+
description=_CLI_DESCRIPTION,
|
|
250
|
+
epilog=_CLI_EPILOG,
|
|
251
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
252
|
+
prog="okstra model")
|
|
239
253
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
240
254
|
listed = sub.add_parser("list")
|
|
241
255
|
listed.add_argument("--role")
|
|
@@ -12,6 +12,12 @@ import subprocess
|
|
|
12
12
|
from functools import lru_cache
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
|
+
from .json_boundary import (
|
|
16
|
+
JsonBoundaryError,
|
|
17
|
+
external_codex_catalog_json_source,
|
|
18
|
+
load_external_json,
|
|
19
|
+
)
|
|
20
|
+
|
|
15
21
|
# Per-role reasoning-effort policy for CLIs that bake effort into the model
|
|
16
22
|
# name (agy). Deterministic — no per-run improvisation.
|
|
17
23
|
# `critic` is intentionally absent: an opt-in critic reuses its worker's
|
|
@@ -30,37 +36,16 @@ def role_effort(role: str) -> str:
|
|
|
30
36
|
return ROLE_EFFORT.get(role, _DEFAULT_EFFORT)
|
|
31
37
|
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# "Gemini 3.6 Flash", while `gemini-3.1-pro-low` correctly identifies as
|
|
36
|
-
# "Gemini 3.1 Pro". The substitution is not visible anywhere in the dispatch —
|
|
37
|
-
# `agy models` lists the slug and the run exits 0 — so it surfaces only as a
|
|
38
|
-
# verifier that never refutes anything: measured over one 63-item plan-body
|
|
39
|
-
# prompt, the high slug returned all-AGREE in 64s while the low slug spent 328s
|
|
40
|
-
# and raised three DISAGREEs, one of them the defect claude and codex both
|
|
41
|
-
# caught that round. Only the `high` tier is affected, so a role asking for any
|
|
42
|
-
# other effort still resolves normally (and a tier agy does not offer still
|
|
43
|
-
# hard-fails below).
|
|
44
|
-
_UNTRUSTED_HIGH_TIER_EXECUTIONS = frozenset({"gemini-3.1-pro"})
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _dispatch_effort(execution: str, role: str) -> str:
|
|
48
|
-
effort = role_effort(role).lower()
|
|
49
|
-
if effort == "high" and execution in _UNTRUSTED_HIGH_TIER_EXECUTIONS:
|
|
50
|
-
return "low"
|
|
51
|
-
return effort
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def dispatch_tier_suffixes() -> frozenset[str]:
|
|
55
|
-
"""Every tier suffix dispatch can append to an agy execution value.
|
|
39
|
+
def agy_tier_suffixes() -> frozenset[str]:
|
|
40
|
+
"""agy 가 모델 식별자에 담는 티어 어휘.
|
|
56
41
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
42
|
+
서빙 식별자에서 티어를 벗겨 카탈로그 id 로 되돌릴 때 이 집합만 벗긴다 —
|
|
43
|
+
마지막 하이픈 뒤를 무엇이든 벗기면 카탈로그에 없는 id 를 만들어 낸다.
|
|
44
|
+
이 집합은 okstra 의 역할별 effort 정책(`ROLE_EFFORT`)이 아니라 agy 의
|
|
45
|
+
어휘다. 둘은 다르다: 정책이 지금 붙이는 것은 `high` 뿐이지만, 관측값에는
|
|
46
|
+
다른 티어가 실려 올 수 있다.
|
|
61
47
|
"""
|
|
62
|
-
|
|
63
|
-
return frozenset(efforts | {"low"})
|
|
48
|
+
return frozenset({"high", "medium", "low"})
|
|
64
49
|
|
|
65
50
|
|
|
66
51
|
@lru_cache(maxsize=1)
|
|
@@ -84,28 +69,42 @@ def agy_models(agy_bin: str = "agy") -> tuple[str, ...]:
|
|
|
84
69
|
return tuple(model_id for model_id in ids if model_id)
|
|
85
70
|
|
|
86
71
|
|
|
87
|
-
def
|
|
88
|
-
return Path.home() / ".codex"
|
|
72
|
+
def _codex_home() -> Path:
|
|
73
|
+
return Path.home() / ".codex"
|
|
89
74
|
|
|
90
75
|
|
|
91
|
-
def codex_availability(
|
|
92
|
-
"""
|
|
93
|
-
|
|
76
|
+
def codex_availability(codex_home: Path | None = None) -> tuple[str, ...]:
|
|
77
|
+
"""Model slugs the codex CLI last fetched from the provider, or ().
|
|
78
|
+
|
|
79
|
+
The earlier source, `[tui.model_availability_nux]` in `config.toml`, is a
|
|
80
|
+
per-model onboarding-notice ledger, not an account's model list: measured
|
|
81
|
+
2026-08-26 that section held `gpt-5.5` and `gpt-5.6-sol` while this machine's
|
|
82
|
+
session history recorded 2072 turns on `gpt-5.6-terra` and 130 on
|
|
83
|
+
`gpt-5.6-luna`. Gating dispatch on it rejected models the CLI runs.
|
|
84
|
+
|
|
85
|
+
`visibility` is not filtered. It governs the CLI's own model picker, not
|
|
86
|
+
whether `--model <slug>` is accepted -- `codex-auto-review` is `hide` and is
|
|
87
|
+
dispatched by okstra today.
|
|
88
|
+
"""
|
|
89
|
+
home = codex_home or _codex_home()
|
|
94
90
|
try:
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
payload = load_external_json(
|
|
92
|
+
external_codex_catalog_json_source(home / "models_cache.json", home),
|
|
93
|
+
artifact="codex model catalog",
|
|
94
|
+
)
|
|
95
|
+
except JsonBoundaryError:
|
|
96
|
+
# No cache yet, or one this process may not read. Returning () turns the
|
|
97
|
+
# gate off rather than blocking dispatch, which is what every other
|
|
98
|
+
# discovery failure in this module does.
|
|
99
|
+
return ()
|
|
100
|
+
models = payload.get("models") if isinstance(payload, dict) else None
|
|
101
|
+
if not isinstance(models, list):
|
|
97
102
|
return ()
|
|
98
103
|
out: list[str] = []
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
in_section = line == "[tui.model_availability_nux]"
|
|
104
|
-
continue
|
|
105
|
-
if in_section and "=" in line:
|
|
106
|
-
key = line.split("=", 1)[0].strip().strip('"')
|
|
107
|
-
if key:
|
|
108
|
-
out.append(key)
|
|
104
|
+
for entry in models:
|
|
105
|
+
slug = entry.get("slug") if isinstance(entry, dict) else None
|
|
106
|
+
if isinstance(slug, str) and slug.strip():
|
|
107
|
+
out.append(slug.strip())
|
|
109
108
|
return tuple(out)
|
|
110
109
|
|
|
111
110
|
|
|
@@ -150,7 +149,7 @@ def normalize_antigravity_execution(
|
|
|
150
149
|
# (`gemini-3.1-pro-high`) and rejects the bare slug with "requires --effort"
|
|
151
150
|
# — a flag the wrapper never sends. So the suffixed form is the only
|
|
152
151
|
# dispatchable spelling, including on the un-verified fallback below.
|
|
153
|
-
tiered = f"{execution}-{
|
|
152
|
+
tiered = f"{execution}-{role_effort(role).lower()}"
|
|
154
153
|
if not available:
|
|
155
154
|
return tiered # discovery unavailable → dispatch anyway, wrapper still guards
|
|
156
155
|
if execution in available:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""`okstra model-io` 의 구현. 진입점은 `..model_io_cli` 다."""
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""읽은 값을 고정 Markdown 줄로 바꾼다.
|
|
2
|
+
|
|
3
|
+
이 층은 파일을 열지 않는다 — 이미 읽힌 매핑을 받아 문자열만 만든다. 그래서
|
|
4
|
+
참조 해석과 독립이고, 서식을 고칠 때 경로 규칙을 다시 읽을 필요가 없다.
|
|
5
|
+
`_provider_models` 만 예외적으로 거절하지만, 그것도 디스크가 아니라 받은
|
|
6
|
+
매핑 안의 모순을 보는 것이다.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Mapping
|
|
12
|
+
|
|
13
|
+
from ..fixed_text import line as _line, scalar as _value
|
|
14
|
+
from ..json_boundary import JsonBoundaryError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _mapping(payload: Mapping[str, Any], key: str) -> Mapping[str, Any]:
|
|
18
|
+
value = payload.get(key)
|
|
19
|
+
return value if isinstance(value, Mapping) else {}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _project_policy_lines(project: Mapping[str, Any]) -> str:
|
|
23
|
+
packs = project.get("reviewRulePacks")
|
|
24
|
+
pack_lines = (
|
|
25
|
+
[
|
|
26
|
+
_line("Review rule pack", path)
|
|
27
|
+
for path in packs
|
|
28
|
+
if isinstance(path, str)
|
|
29
|
+
]
|
|
30
|
+
if isinstance(packs, list)
|
|
31
|
+
else []
|
|
32
|
+
)
|
|
33
|
+
qa_lines: list[str] = []
|
|
34
|
+
qa_commands = project.get("qaCommands")
|
|
35
|
+
if isinstance(qa_commands, Mapping):
|
|
36
|
+
for category, entries in sorted(qa_commands.items()):
|
|
37
|
+
if not isinstance(entries, list):
|
|
38
|
+
continue
|
|
39
|
+
for entry in entries:
|
|
40
|
+
if not isinstance(entry, Mapping):
|
|
41
|
+
continue
|
|
42
|
+
qa_lines.append(
|
|
43
|
+
f"- QA `{_value(category)}` / `{_value(entry.get('label'))}`: "
|
|
44
|
+
f"`{_value(entry.get('cmd'))}`\n"
|
|
45
|
+
)
|
|
46
|
+
return (
|
|
47
|
+
"\n## Project Review Rule Packs\n\n"
|
|
48
|
+
+ ("".join(pack_lines) or "- None\n")
|
|
49
|
+
+ "\n## Project QA Commands\n\n"
|
|
50
|
+
+ ("".join(qa_lines) or "- None\n")
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _assignment_line(prefix: str, assignment: Mapping[str, Any], identifier: object = None) -> str:
|
|
55
|
+
label = prefix if identifier is None else f"{prefix} `{_value(identifier)}`"
|
|
56
|
+
return (
|
|
57
|
+
f"- {label}: role `{_value(assignment.get('role'))}`; "
|
|
58
|
+
f"provider `{_value(assignment.get('provider'))}`; "
|
|
59
|
+
f"model `{_value(assignment.get('model'))}`\n"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _worker_assignment_lines(run: Mapping[str, Any]) -> str:
|
|
64
|
+
assignments = run.get("workerAssignments")
|
|
65
|
+
if not isinstance(assignments, list):
|
|
66
|
+
return "- None\n"
|
|
67
|
+
lines = [
|
|
68
|
+
_assignment_line("Worker", assignment, assignment.get("workerId"))
|
|
69
|
+
for assignment in assignments
|
|
70
|
+
if isinstance(assignment, Mapping)
|
|
71
|
+
]
|
|
72
|
+
return "".join(lines) or "- None\n"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _worker_prompt_lines(run: Mapping[str, Any]) -> str:
|
|
76
|
+
paths = run.get("workerPromptPathByWorkerId")
|
|
77
|
+
if not isinstance(paths, Mapping):
|
|
78
|
+
return "- None\n"
|
|
79
|
+
lines = [
|
|
80
|
+
f"- Worker prompt `{_value(worker_id)}`: `{_value(path)}`\n"
|
|
81
|
+
for worker_id, path in sorted(paths.items(), key=lambda item: _value(item[0]))
|
|
82
|
+
]
|
|
83
|
+
return "".join(lines) or "- None\n"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _next_phase_lines(workflow: Mapping[str, Any]) -> str:
|
|
87
|
+
pointer = _mapping(workflow, "nextRecommendedPhase")
|
|
88
|
+
return (
|
|
89
|
+
_line("Next phase", pointer.get("phase"))
|
|
90
|
+
+ _line("Next phase status", pointer.get("status"))
|
|
91
|
+
+ _line("Next phase rationale", pointer.get("rationale"))
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _status_overview_text(rows: list[dict[str, object]]) -> str:
|
|
96
|
+
blocks = [_line("Task count", len(rows))]
|
|
97
|
+
for row in rows:
|
|
98
|
+
blocks.extend((
|
|
99
|
+
"\n## Task\n\n",
|
|
100
|
+
_line("Task key", row.get("taskKey")),
|
|
101
|
+
_line("Task group", row.get("taskGroup")),
|
|
102
|
+
_line("Task type", row.get("taskType")),
|
|
103
|
+
_line("Latest run status", row.get("latestRunStatus")),
|
|
104
|
+
_line("Updated at", row.get("updatedAt")),
|
|
105
|
+
_line("Latest run manifest", row.get("latestRunManifestPath")),
|
|
106
|
+
_line("Work category", row.get("workCategory")),
|
|
107
|
+
_line("Current status", row.get("currentStatus")),
|
|
108
|
+
_line("Current phase", row.get("currentPhase")),
|
|
109
|
+
_line("Current phase state", row.get("currentPhaseState")),
|
|
110
|
+
_line("Next phase", row.get("nextPhase")),
|
|
111
|
+
_line("Next phase status", row.get("nextPhaseStatus")),
|
|
112
|
+
_line("Next phase rationale", row.get("nextPhaseRationale")),
|
|
113
|
+
_line("Awaiting approval", row.get("awaitingApproval")),
|
|
114
|
+
_line("Latest report", row.get("latestReportRecordPath")),
|
|
115
|
+
_line("Latest resume command", row.get("latestResumeCommandPath")),
|
|
116
|
+
_line("Work status", row.get("workStatus")),
|
|
117
|
+
))
|
|
118
|
+
return "# Okstra Status Input\n\n" + "".join(blocks)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _history_overview_text(rows: list[dict[str, object]], limit: int) -> str:
|
|
122
|
+
shown = rows[:limit]
|
|
123
|
+
blocks = [
|
|
124
|
+
_line("Task count", len(rows)),
|
|
125
|
+
_line("Shown task count", len(shown)),
|
|
126
|
+
_line("Remaining task count", max(0, len(rows) - len(shown))),
|
|
127
|
+
]
|
|
128
|
+
for row in shown:
|
|
129
|
+
blocks.extend((
|
|
130
|
+
"\n## Task\n\n", _line("Task key", row.get("taskKey")),
|
|
131
|
+
_line("Task group", row.get("taskGroup")),
|
|
132
|
+
_line("Task type", row.get("taskType")),
|
|
133
|
+
_line("Current status", row.get("currentStatus")),
|
|
134
|
+
_line("Latest run status", row.get("latestRunStatus")),
|
|
135
|
+
_line("Last run", row.get("lastRun")),
|
|
136
|
+
_line("Latest report", row.get("latestReportRecordPath")),
|
|
137
|
+
_line("Updated at", row.get("updatedAt")),
|
|
138
|
+
_line("Latest run manifest", row.get("latestRunManifestPath")),
|
|
139
|
+
))
|
|
140
|
+
return "# Okstra History Input\n\n" + "".join(blocks)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _csv(value: object) -> str:
|
|
144
|
+
return ",".join(str(item) for item in value) if isinstance(value, list) else ""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _provider_models(run: Mapping[str, Any]) -> dict[str, str]:
|
|
148
|
+
values: dict[str, set[str]] = {}
|
|
149
|
+
assignments = run.get("workerAssignments")
|
|
150
|
+
if isinstance(assignments, list):
|
|
151
|
+
for assignment in assignments:
|
|
152
|
+
if not isinstance(assignment, Mapping):
|
|
153
|
+
continue
|
|
154
|
+
provider = assignment.get("provider")
|
|
155
|
+
model = assignment.get("model")
|
|
156
|
+
if isinstance(provider, str) and isinstance(model, str) and model:
|
|
157
|
+
values.setdefault(provider, set()).add(model)
|
|
158
|
+
for provider, models in values.items():
|
|
159
|
+
if len(models) > 1:
|
|
160
|
+
raise JsonBoundaryError(
|
|
161
|
+
Path("workerAssignments"), "run manifest", f"conflicting models for provider {provider}"
|
|
162
|
+
)
|
|
163
|
+
return {provider: next(iter(models)) for provider, models in values.items()}
|