okstra 0.180.0 → 0.184.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.mjs +16 -2
- package/dist/cli-registry.mjs.map +1 -1
- package/dist/commands/execute/render-bundle.d.mts +4 -2
- package/dist/commands/execute/render-bundle.mjs +46 -5
- package/dist/commands/execute/render-bundle.mjs.map +1 -1
- package/dist/commands/execute/run.mjs +11 -3
- package/dist/commands/execute/run.mjs.map +1 -1
- package/dist/commands/inspect/model-io.d.mts +1 -0
- package/dist/commands/inspect/model-io.mjs +25 -0
- package/dist/commands/inspect/model-io.mjs.map +1 -0
- package/dist/commands/inspect/stage-map.mjs +29 -8
- package/dist/commands/inspect/stage-map.mjs.map +1 -1
- package/dist/commands/inspect/task-list.mjs +52 -6
- package/dist/commands/inspect/task-list.mjs.map +1 -1
- package/dist/commands/inspect/user-response.mjs +14 -4
- package/dist/commands/inspect/user-response.mjs.map +1 -1
- package/dist/commands/lifecycle/check-project.d.mts +1 -0
- package/dist/commands/lifecycle/check-project.mjs +69 -50
- package/dist/commands/lifecycle/check-project.mjs.map +1 -1
- package/dist/commands/lifecycle/contract-check.d.mts +1 -0
- package/dist/commands/lifecycle/contract-check.mjs +18 -0
- package/dist/commands/lifecycle/contract-check.mjs.map +1 -0
- package/dist/commands/lifecycle/preflight.mjs +154 -51
- package/dist/commands/lifecycle/preflight.mjs.map +1 -1
- package/dist/commands/pr/pr.d.mts +1 -0
- package/dist/commands/pr/pr.mjs +19 -1
- package/dist/commands/pr/pr.mjs.map +1 -1
- package/dist/commands/report/agent-activity.mjs +2 -2
- package/dist/commands/report/translate.mjs +3 -0
- package/dist/commands/report/translate.mjs.map +1 -1
- package/dist/lib/host-registry-client.mjs +13 -9
- package/dist/lib/host-registry-client.mjs.map +1 -1
- package/docs/architecture.md +13 -2
- package/docs/cli.md +31 -15
- package/docs/container.md +6 -4
- package/docs/contributor-change-matrix.md +1 -1
- package/docs/for-ai/README.md +2 -2
- package/docs/for-ai/skills/okstra-brief-gen.md +5 -3
- package/docs/for-ai/skills/okstra-code-review.md +4 -4
- package/docs/for-ai/skills/okstra-container-build.md +20 -17
- package/docs/for-ai/skills/okstra-inspect.md +20 -23
- package/docs/for-ai/skills/okstra-manager.md +19 -18
- package/docs/for-ai/skills/okstra-memory.md +2 -2
- package/docs/for-ai/skills/okstra-pr-gen.md +3 -3
- package/docs/for-ai/skills/okstra-rollup.md +14 -13
- package/docs/for-ai/skills/okstra-run.md +7 -3
- package/docs/for-ai/skills/okstra-schedule-gen.md +15 -18
- package/docs/for-ai/skills/okstra-setup.md +7 -7
- package/docs/for-ai/skills/okstra-usage.md +5 -4
- package/docs/for-ai/skills/okstra-user-response.md +50 -32
- package/docs/project-structure-overview.md +30 -27
- package/docs/task-process/README.md +1 -1
- package/docs/task-process/common-flow.md +2 -3
- package/docs/task-process/error-analysis.md +3 -4
- package/docs/task-process/final-verification.md +2 -3
- package/docs/task-process/implementation-planning.md +2 -3
- package/docs/task-process/implementation.md +9 -7
- package/docs/task-process/release-handoff.md +3 -4
- package/docs/task-process/requirements-discovery.md +3 -4
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +4 -4
- package/runtime/agents/workers/report-writer-worker.md +3 -3
- package/runtime/agents/workers/translator-worker.md +5 -13
- package/runtime/bin/okstra-error-log.py +51 -11
- package/runtime/bin/okstra-report-translate.py +210 -23
- package/runtime/prompts/host-orchestration/implementation.md +1 -1
- package/runtime/prompts/launch.template.md +11 -14
- package/runtime/prompts/lead/context-loader.md +41 -141
- package/runtime/prompts/lead/convergence.md +8 -6
- package/runtime/prompts/lead/okstra-lead-contract.md +26 -36
- package/runtime/prompts/lead/plan-body-verification.md +211 -30
- package/runtime/prompts/lead/report-writer.md +23 -4
- package/runtime/prompts/lead/team-contract.md +8 -53
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +3 -2
- package/runtime/prompts/profiles/_common-contract.md +1 -1
- package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
- package/runtime/prompts/profiles/_implementation-executor.md +1 -0
- package/runtime/prompts/profiles/_implementation-verifier.md +4 -4
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +17 -13
- package/runtime/prompts/profiles/release-handoff.md +0 -1
- package/runtime/prompts/wizard/prompts.ko.json +7 -11
- package/runtime/python/okstra_ctl/adapters/hosts/capability_adapter.py +69 -17
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +13 -4
- package/runtime/python/okstra_ctl/adapters/hosts/claude-code/relay.md +6 -1
- package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/codex/relay.md +50 -5
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
- package/runtime/python/okstra_ctl/adapters/hosts/grok/relay.md +66 -5
- package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +70 -2
- package/runtime/python/okstra_ctl/agent_activity.py +118 -35
- package/runtime/python/okstra_ctl/agent_invocation.py +19 -6
- package/runtime/python/okstra_ctl/agent_prompt_cli.py +65 -18
- package/runtime/python/okstra_ctl/analysis_inputs.py +5 -4
- package/runtime/python/okstra_ctl/analysis_packet.py +81 -1
- package/runtime/python/okstra_ctl/approval_decisions.py +3 -2
- package/runtime/python/okstra_ctl/attempt_evidence.py +2 -2
- package/runtime/python/okstra_ctl/backfill.py +13 -10
- package/runtime/python/okstra_ctl/batch.py +2 -4
- package/runtime/python/okstra_ctl/build_tools.py +6 -3
- package/runtime/python/okstra_ctl/claim_reproduction.py +101 -0
- package/runtime/python/okstra_ctl/clarification_items.py +27 -13
- package/runtime/python/okstra_ctl/cmux.py +130 -52
- package/runtime/python/okstra_ctl/code_review_target.py +34 -8
- package/runtime/python/okstra_ctl/conformance.py +37 -1
- package/runtime/python/okstra_ctl/consumers.py +5 -4
- package/runtime/python/okstra_ctl/container.py +103 -8
- package/runtime/python/okstra_ctl/context_cost.py +2 -1
- package/runtime/python/okstra_ctl/contract_graph.py +497 -0
- package/runtime/python/okstra_ctl/contract_graph_cli.py +62 -0
- package/runtime/python/okstra_ctl/convergence.py +338 -17
- package/runtime/python/okstra_ctl/convergence_engine.py +10 -18
- package/runtime/python/okstra_ctl/convergence_provenance.py +58 -8
- package/runtime/python/okstra_ctl/convergence_store.py +55 -34
- package/runtime/python/okstra_ctl/design_prep.py +7 -4
- package/runtime/python/okstra_ctl/dispatch_core.py +35 -65
- package/runtime/python/okstra_ctl/dispatch_state.py +134 -59
- package/runtime/python/okstra_ctl/doctor.py +6 -3
- package/runtime/python/okstra_ctl/domain/worker_presentation.py +70 -9
- package/runtime/python/okstra_ctl/entrypoints/hosts.py +16 -30
- package/runtime/python/okstra_ctl/error_log_write.py +35 -30
- package/runtime/python/okstra_ctl/error_report.py +26 -1
- package/runtime/python/okstra_ctl/error_zip.py +27 -5
- package/runtime/python/okstra_ctl/execution_identity.py +3 -2
- package/runtime/python/okstra_ctl/execution_manifest.py +7 -4
- package/runtime/python/okstra_ctl/final_report_schema.py +2 -2
- package/runtime/python/okstra_ctl/fix_cycles.py +2 -2
- package/runtime/python/okstra_ctl/fixed_text.py +39 -0
- package/runtime/python/okstra_ctl/git_reconcile.py +41 -9
- package/runtime/python/okstra_ctl/handoff.py +5 -4
- package/runtime/python/okstra_ctl/i18n.py +4 -2
- package/runtime/python/okstra_ctl/implementation_direction.py +22 -14
- package/runtime/python/okstra_ctl/implementation_outcome.py +4 -7
- package/runtime/python/okstra_ctl/incremental_carry.py +2 -1
- package/runtime/python/okstra_ctl/incremental_scope.py +89 -39
- package/runtime/python/okstra_ctl/index.py +8 -11
- package/runtime/python/okstra_ctl/initial_prompt_materialization.py +79 -7
- package/runtime/python/okstra_ctl/invocation.py +3 -6
- package/runtime/python/okstra_ctl/json_boundary.py +366 -0
- package/runtime/python/okstra_ctl/json_registry.py +10 -12
- package/runtime/python/okstra_ctl/jsonl.py +19 -2
- package/runtime/python/okstra_ctl/lead_events.py +33 -1
- package/runtime/python/okstra_ctl/listing.py +3 -3
- package/runtime/python/okstra_ctl/log_report.py +24 -2
- package/runtime/python/okstra_ctl/manager_cli.py +92 -7
- package/runtime/python/okstra_ctl/manager_store.py +12 -10
- package/runtime/python/okstra_ctl/material.py +5 -1
- package/runtime/python/okstra_ctl/migrate.py +29 -25
- package/runtime/python/okstra_ctl/model_cli.py +3 -15
- package/runtime/python/okstra_ctl/model_io_cli.py +1051 -0
- package/runtime/python/okstra_ctl/mutation_probe.py +13 -4
- package/runtime/python/okstra_ctl/pane_reclaim.py +3 -2
- package/runtime/python/okstra_ctl/paths.py +9 -0
- package/runtime/python/okstra_ctl/plan_items.py +525 -5
- package/runtime/python/okstra_ctl/plan_items_cli.py +842 -32
- package/runtime/python/okstra_ctl/pr_template.py +3 -2
- package/runtime/python/okstra_ctl/project_meta.py +5 -7
- package/runtime/python/okstra_ctl/recap.py +5 -4
- package/runtime/python/okstra_ctl/reconcile.py +21 -27
- package/runtime/python/okstra_ctl/registry/host_discovery.py +3 -2
- package/runtime/python/okstra_ctl/registry/provider_registry.py +3 -2
- package/runtime/python/okstra_ctl/render.py +30 -15
- package/runtime/python/okstra_ctl/render_final_report.py +3 -2
- package/runtime/python/okstra_ctl/report_assembly.py +172 -17
- package/runtime/python/okstra_ctl/report_finalize.py +7 -10
- package/runtime/python/okstra_ctl/report_html/render.py +3 -2
- package/runtime/python/okstra_ctl/report_language.py +3 -2
- package/runtime/python/okstra_ctl/report_markdown.py +13 -1
- package/runtime/python/okstra_ctl/report_narrative.py +40 -8
- package/runtime/python/okstra_ctl/report_synthesis_packet.py +518 -0
- package/runtime/python/okstra_ctl/report_views.py +3 -2
- package/runtime/python/okstra_ctl/rollup.py +65 -4
- package/runtime/python/okstra_ctl/run.py +159 -56
- package/runtime/python/okstra_ctl/run_audit.py +3 -2
- package/runtime/python/okstra_ctl/run_context.py +6 -9
- package/runtime/python/okstra_ctl/run_index_row.py +2 -8
- package/runtime/python/okstra_ctl/schedule_semantics.py +5 -2
- package/runtime/python/okstra_ctl/schema_excerpt.py +4 -2
- package/runtime/python/okstra_ctl/session_transcript.py +27 -1
- package/runtime/python/okstra_ctl/set_work_status.py +64 -38
- package/runtime/python/okstra_ctl/stage_fix_carry.py +4 -2
- package/runtime/python/okstra_ctl/stage_map.py +26 -6
- package/runtime/python/okstra_ctl/stage_targets.py +3 -4
- package/runtime/python/okstra_ctl/team.py +2 -1
- package/runtime/python/okstra_ctl/team_reconcile.py +11 -2
- package/runtime/python/okstra_ctl/time_report.py +51 -4
- package/runtime/python/okstra_ctl/usage_identity.py +2 -1
- package/runtime/python/okstra_ctl/usage_report.py +58 -4
- package/runtime/python/okstra_ctl/user_response.py +1431 -66
- package/runtime/python/okstra_ctl/wizard.py +50 -117
- package/runtime/python/okstra_ctl/work_categories.py +3 -2
- package/runtime/python/okstra_ctl/worker_prompt_body.py +18 -7
- package/runtime/python/okstra_ctl/worker_prompt_contract.py +3 -2
- package/runtime/python/okstra_ctl/worker_runner.py +14 -12
- package/runtime/python/okstra_ctl/workflow.py +2 -1
- package/runtime/python/okstra_ctl/worktree.py +3 -2
- package/runtime/python/okstra_ctl/wrapper_status.py +4 -2
- package/runtime/python/okstra_ctl/write_policy.py +4 -2
- package/runtime/python/okstra_token_usage/antigravity.py +39 -12
- package/runtime/python/okstra_token_usage/collect.py +90 -38
- package/runtime/python/okstra_token_usage/grok.py +127 -0
- package/runtime/schemas/final-report-v2.0.schema.json +21 -0
- package/runtime/schemas/final-report-v3.0.schema.json +21 -0
- package/runtime/schemas/report-synthesis-packet-v1.0.schema.json +140 -0
- package/runtime/skills/okstra-brief-gen/SKILL.md +9 -7
- package/runtime/skills/okstra-code-review/SKILL.md +21 -11
- package/runtime/skills/okstra-container-build/SKILL.md +18 -18
- package/runtime/skills/okstra-inspect/SKILL.md +12 -11
- package/runtime/skills/okstra-inspect/facets/error-zip.md +8 -8
- package/runtime/skills/okstra-inspect/facets/errors.md +2 -2
- package/runtime/skills/okstra-inspect/facets/history.md +9 -14
- package/runtime/skills/okstra-inspect/facets/logs.md +2 -2
- package/runtime/skills/okstra-inspect/facets/recap.md +5 -5
- package/runtime/skills/okstra-inspect/facets/report.md +6 -10
- package/runtime/skills/okstra-inspect/facets/status.md +9 -8
- package/runtime/skills/okstra-inspect/facets/time.md +3 -3
- package/runtime/skills/okstra-manager/SKILL.md +16 -14
- package/runtime/skills/okstra-memory/SKILL.md +3 -3
- package/runtime/skills/okstra-pr-gen/SKILL.md +5 -4
- package/runtime/skills/okstra-rollup/SKILL.md +6 -16
- package/runtime/skills/okstra-run/SKILL.md +9 -9
- package/runtime/skills/okstra-schedule-gen/SKILL.md +21 -17
- package/runtime/skills/okstra-setup/SKILL.md +21 -13
- package/runtime/skills/okstra-setup/references/project-config.md +2 -2
- package/runtime/skills/okstra-usage/SKILL.md +10 -10
- package/runtime/skills/okstra-user-response/SKILL.md +78 -107
- package/runtime/templates/report-writer-prompt-preamble.md +17 -1
- package/runtime/templates/reports/schedule.template.md +4 -4
- package/runtime/templates/worker-error-contract.md +17 -29
- package/runtime/validators/validate-run.py +527 -113
- package/runtime/validators/validate_session_conformance.py +65 -10
|
@@ -14,16 +14,17 @@ from .claude import (
|
|
|
14
14
|
find_claude_team_sessions,
|
|
15
15
|
)
|
|
16
16
|
from .codex import codex_session_total, find_codex_sessions
|
|
17
|
-
from .antigravity import
|
|
18
|
-
|
|
19
|
-
antigravity_session_total,
|
|
20
|
-
find_antigravity_sessions,
|
|
21
|
-
)
|
|
17
|
+
from .antigravity import antigravity_session_total, find_antigravity_sessions
|
|
18
|
+
from .grok import find_grok_sessions, grok_session_total
|
|
22
19
|
from .paths import claude_project_dir, utc_now
|
|
23
20
|
from .pricing import antigravity_cost_usd, provider_cost_usd
|
|
24
21
|
from okstra_ctl.dispatch_state import worker_session_ids
|
|
25
22
|
from okstra_ctl.models import provider_wrappers
|
|
26
|
-
from okstra_ctl.wrapper_status import
|
|
23
|
+
from okstra_ctl.wrapper_status import (
|
|
24
|
+
log_path_for_prompt,
|
|
25
|
+
read_wrapper_status,
|
|
26
|
+
status_path_for_prompt,
|
|
27
|
+
)
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def match_prefixes(worker_id: str) -> list[str]:
|
|
@@ -472,6 +473,8 @@ def _cli_sessions_for_windows(
|
|
|
472
473
|
matches = find_codex_sessions(project_root, started_at, ended_at)
|
|
473
474
|
elif provider == "antigravity":
|
|
474
475
|
matches = find_antigravity_sessions(project_root, started_at, ended_at)
|
|
476
|
+
elif provider == "grok":
|
|
477
|
+
matches = find_grok_sessions(project_root, started_at, ended_at)
|
|
475
478
|
else:
|
|
476
479
|
matches = []
|
|
477
480
|
for path in matches:
|
|
@@ -488,6 +491,8 @@ def _cli_session_totals(provider: str, session_paths: list[Path]) -> list[dict]:
|
|
|
488
491
|
total = codex_session_total(session_path)
|
|
489
492
|
elif provider == "antigravity":
|
|
490
493
|
total = antigravity_session_total(session_path)
|
|
494
|
+
elif provider == "grok":
|
|
495
|
+
total = grok_session_total(session_path)
|
|
491
496
|
else:
|
|
492
497
|
continue
|
|
493
498
|
if total.get("available"):
|
|
@@ -582,24 +587,20 @@ def collect_cli_usage(
|
|
|
582
587
|
execution = wrapper_execution(status_path)
|
|
583
588
|
execution_note = _execution_note(execution)
|
|
584
589
|
|
|
585
|
-
|
|
586
|
-
|
|
590
|
+
totals = _cli_session_totals(provider, sessions)
|
|
591
|
+
if not sessions:
|
|
592
|
+
block = na_block(
|
|
593
|
+
f"{execution_note}; CLI usage attribution unavailable because no transcript was found"
|
|
594
|
+
)
|
|
587
595
|
block["cliNote"] = block["note"]
|
|
596
|
+
elif not totals:
|
|
597
|
+
block = na_block(
|
|
598
|
+
f"{execution_note}; transcript found but no final token snapshot was recorded"
|
|
599
|
+
)
|
|
600
|
+
block["cliNote"] = block["note"]
|
|
601
|
+
block["cliSessionPaths"] = [str(path) for path in sessions]
|
|
588
602
|
else:
|
|
589
|
-
|
|
590
|
-
if not sessions:
|
|
591
|
-
block = na_block(
|
|
592
|
-
f"{execution_note}; CLI usage attribution unavailable because no transcript was found"
|
|
593
|
-
)
|
|
594
|
-
block["cliNote"] = block["note"]
|
|
595
|
-
elif not totals:
|
|
596
|
-
block = na_block(
|
|
597
|
-
f"{execution_note}; transcript found but no final token snapshot was recorded"
|
|
598
|
-
)
|
|
599
|
-
block["cliNote"] = block["note"]
|
|
600
|
-
block["cliSessionPaths"] = [str(path) for path in sessions]
|
|
601
|
-
else:
|
|
602
|
-
block = _cli_usage_block(provider, _aggregate_totals(totals), sessions)
|
|
603
|
+
block = _cli_usage_block(provider, _aggregate_totals(totals), sessions)
|
|
603
604
|
|
|
604
605
|
block["cliExecutionStatus"] = execution["status"]
|
|
605
606
|
if fallback_window_used:
|
|
@@ -624,6 +625,51 @@ def _worker_cli_windows(
|
|
|
624
625
|
return fallback_windows, status_path, True
|
|
625
626
|
|
|
626
627
|
|
|
628
|
+
def _antigravity_log_paths(
|
|
629
|
+
project_root: Path,
|
|
630
|
+
worker: dict,
|
|
631
|
+
status_path: Path | None,
|
|
632
|
+
) -> list[Path]:
|
|
633
|
+
"""agy stream-json 은 워커 로그에 남는다. 홈 세션 디렉터리가 아니다."""
|
|
634
|
+
paths: list[Path] = []
|
|
635
|
+
prompt_path = _resolve_project_path(project_root, str(worker.get("promptPath") or ""))
|
|
636
|
+
if prompt_path is not None:
|
|
637
|
+
log_path = log_path_for_prompt(prompt_path)
|
|
638
|
+
if log_path.is_file():
|
|
639
|
+
paths.append(log_path)
|
|
640
|
+
status = read_wrapper_status(status_path) if status_path else None
|
|
641
|
+
if status is not None and status.log_path is not None and status.log_path.is_file():
|
|
642
|
+
if status.log_path not in paths:
|
|
643
|
+
paths.append(status.log_path)
|
|
644
|
+
return paths
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def _worker_cli_usage_block(
|
|
648
|
+
*,
|
|
649
|
+
provider: str,
|
|
650
|
+
project_root: Path,
|
|
651
|
+
worker: dict,
|
|
652
|
+
fallback_windows: list[tuple[str, str]],
|
|
653
|
+
) -> dict:
|
|
654
|
+
"""공급자 프로세스 트랜스크립트와 실행 증거로 워커 사용량 블록을 만든다."""
|
|
655
|
+
windows, status_path, used_fallback = _worker_cli_windows(
|
|
656
|
+
project_root,
|
|
657
|
+
worker,
|
|
658
|
+
fallback_windows,
|
|
659
|
+
)
|
|
660
|
+
session_paths = _cli_sessions_for_windows(provider, project_root, windows)
|
|
661
|
+
if provider == "antigravity":
|
|
662
|
+
for log_path in _antigravity_log_paths(project_root, worker, status_path):
|
|
663
|
+
if log_path not in session_paths:
|
|
664
|
+
session_paths.append(log_path)
|
|
665
|
+
return collect_cli_usage(
|
|
666
|
+
provider=provider,
|
|
667
|
+
status_path=status_path,
|
|
668
|
+
sessions=session_paths,
|
|
669
|
+
fallback_window_used=used_fallback,
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
|
|
627
673
|
def _attach_cli_usage(
|
|
628
674
|
block: dict,
|
|
629
675
|
provider: str,
|
|
@@ -638,8 +684,8 @@ def _attach_cli_usage(
|
|
|
638
684
|
window produces several rollout jsonls; pricing only the latest (the old
|
|
639
685
|
``find_codex_session`` -> ``sessions[-1]`` behavior) dropped the earlier
|
|
640
686
|
attempts' tokens. We sum every in-window session here so the redispatched
|
|
641
|
-
CLI spend is fully reported.
|
|
642
|
-
|
|
687
|
+
CLI spend is fully reported. antigravity 는 워커 로그의 stream-json
|
|
688
|
+
``result.usage`` 를 같은 합산 경로에 붙인다.
|
|
643
689
|
"""
|
|
644
690
|
session_paths = _cli_sessions_for_windows(provider, project_root, windows)
|
|
645
691
|
cli = collect_cli_usage(
|
|
@@ -676,17 +722,11 @@ def _collect_cli_runtime_usage(state: dict, project_root: Path) -> dict:
|
|
|
676
722
|
)
|
|
677
723
|
continue
|
|
678
724
|
worker_id = str(worker.get("workerId") or "").strip()
|
|
679
|
-
|
|
680
|
-
project_root,
|
|
681
|
-
worker,
|
|
682
|
-
windows_by_worker.get(worker_id, []),
|
|
683
|
-
)
|
|
684
|
-
session_paths = _cli_sessions_for_windows(provider, project_root, windows)
|
|
685
|
-
worker["usage"] = collect_cli_usage(
|
|
725
|
+
worker["usage"] = _worker_cli_usage_block(
|
|
686
726
|
provider=provider,
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
727
|
+
project_root=project_root,
|
|
728
|
+
worker=worker,
|
|
729
|
+
fallback_windows=windows_by_worker.get(worker_id, []),
|
|
690
730
|
)
|
|
691
731
|
_populate_usage_summary(state, team_name=resolve_team_name(state),
|
|
692
732
|
sessions_found=0, needle_source="none")
|
|
@@ -871,6 +911,7 @@ def collect_claude_runtime_usage(
|
|
|
871
911
|
sessions_dir = claude_project_dir(cwd)
|
|
872
912
|
# sid 로 귀속한 세션 id 전체 — 아래 unattributed 폴드에서 빼는 데 쓴다.
|
|
873
913
|
attributed_sids: set[str] = set()
|
|
914
|
+
cli_windows_by_worker = _codex_worker_windows(cwd, state)
|
|
874
915
|
for worker in state.get("workers", []):
|
|
875
916
|
worker_id = worker.get("workerId")
|
|
876
917
|
agent = worker.get("agent")
|
|
@@ -922,10 +963,21 @@ def collect_claude_runtime_usage(
|
|
|
922
963
|
matched_sids.add(entry[0])
|
|
923
964
|
|
|
924
965
|
if not matched:
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
966
|
+
provider = _cli_assignment_provider(worker)
|
|
967
|
+
if provider and str(worker.get("runner") or "") == "cli-wrapper":
|
|
968
|
+
worker["usage"] = _worker_cli_usage_block(
|
|
969
|
+
provider=provider,
|
|
970
|
+
project_root=cwd,
|
|
971
|
+
worker=worker,
|
|
972
|
+
fallback_windows=cli_windows_by_worker.get(
|
|
973
|
+
str(worker_id or "").strip(), []
|
|
974
|
+
),
|
|
975
|
+
)
|
|
976
|
+
else:
|
|
977
|
+
worker["usage"] = na_block(
|
|
978
|
+
"no Claude session jsonl in the run window for dispatched "
|
|
979
|
+
f"sessionIds {dispatched_sids} or agentName prefixes {prefixes}"
|
|
980
|
+
)
|
|
929
981
|
continue
|
|
930
982
|
|
|
931
983
|
# Stable order by startedAt so the "primary" session is the first one.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Grok Build session collectors.
|
|
2
|
+
|
|
3
|
+
공식 세션 문서는 대화를 ``~/.grok/sessions/`` 에 둔다. cwd 는 퍼센트 인코딩된
|
|
4
|
+
디렉터리 이름이다. 누적 사용량은 ``updates.jsonl`` 의
|
|
5
|
+
``params.update.usage.modelUsage`` 마지막 스냅샷이다.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from datetime import datetime, timezone
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Mapping
|
|
13
|
+
from urllib.parse import quote, unquote
|
|
14
|
+
|
|
15
|
+
from .jsonl_io import iter_jsonl
|
|
16
|
+
from .paths import ts_in_window
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def grok_sessions_root(
|
|
20
|
+
home: Path | None = None,
|
|
21
|
+
env: Mapping[str, str] | None = None,
|
|
22
|
+
) -> Path:
|
|
23
|
+
environ = env if env is not None else os.environ
|
|
24
|
+
configured = str(environ.get("GROK_HOME") or "").strip()
|
|
25
|
+
base = Path(configured).expanduser() if configured else (home or Path.home()) / ".grok"
|
|
26
|
+
return base / "sessions"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _iso_from_unix(value: object) -> str | None:
|
|
30
|
+
if not isinstance(value, (int, float)) or isinstance(value, bool):
|
|
31
|
+
return None
|
|
32
|
+
return datetime.fromtimestamp(value, timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _usage_payload(record: dict) -> dict | None:
|
|
36
|
+
params = record.get("params")
|
|
37
|
+
if not isinstance(params, dict):
|
|
38
|
+
return None
|
|
39
|
+
update = params.get("update")
|
|
40
|
+
if not isinstance(update, dict):
|
|
41
|
+
return None
|
|
42
|
+
usage = update.get("usage")
|
|
43
|
+
return usage if isinstance(usage, dict) else None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _model_snapshot(usage: dict) -> tuple[dict, str | None]:
|
|
47
|
+
models = usage.get("modelUsage")
|
|
48
|
+
if isinstance(models, dict) and len(models) == 1:
|
|
49
|
+
name, payload = next(iter(models.items()))
|
|
50
|
+
if isinstance(payload, dict):
|
|
51
|
+
return payload, name if isinstance(name, str) else None
|
|
52
|
+
return usage, None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def grok_session_total(updates_path: Path) -> dict:
|
|
56
|
+
"""마지막 modelUsage 스냅샷. 세션 누적이라 더하지 않는다."""
|
|
57
|
+
last: dict | None = None
|
|
58
|
+
model: str | None = None
|
|
59
|
+
started: str | None = None
|
|
60
|
+
ended: str | None = None
|
|
61
|
+
for record in iter_jsonl(updates_path):
|
|
62
|
+
usage = _usage_payload(record)
|
|
63
|
+
if usage is None:
|
|
64
|
+
continue
|
|
65
|
+
iso = _iso_from_unix(record.get("timestamp"))
|
|
66
|
+
if iso and started is None:
|
|
67
|
+
started = iso
|
|
68
|
+
if iso:
|
|
69
|
+
ended = iso
|
|
70
|
+
snapshot, snapshot_model = _model_snapshot(usage)
|
|
71
|
+
last = snapshot
|
|
72
|
+
if snapshot_model:
|
|
73
|
+
model = snapshot_model
|
|
74
|
+
if last is None:
|
|
75
|
+
return {"totalTokens": 0, "available": False}
|
|
76
|
+
return {
|
|
77
|
+
"totalTokens": last.get("totalTokens", 0) or 0,
|
|
78
|
+
"inputTokens": last.get("inputTokens", 0) or 0,
|
|
79
|
+
"outputTokens": last.get("outputTokens", 0) or 0,
|
|
80
|
+
"cachedInputTokens": last.get("cachedReadTokens", 0) or 0,
|
|
81
|
+
"reasoningOutputTokens": last.get("reasoningTokens", 0) or 0,
|
|
82
|
+
"durationMs": last.get("apiDurationMs", 0) or 0,
|
|
83
|
+
"model": model,
|
|
84
|
+
"startedAt": started,
|
|
85
|
+
"endedAt": ended,
|
|
86
|
+
"available": True,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _session_in_window(updates_path: Path, started_at: str, ended_at: str) -> bool:
|
|
91
|
+
for record in iter_jsonl(updates_path):
|
|
92
|
+
iso = _iso_from_unix(record.get("timestamp"))
|
|
93
|
+
if iso and ts_in_window(iso, started_at, ended_at):
|
|
94
|
+
return True
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def find_grok_sessions(
|
|
99
|
+
cwd: Path,
|
|
100
|
+
started_at: str,
|
|
101
|
+
ended_at: str,
|
|
102
|
+
*,
|
|
103
|
+
session_root: Path | None = None,
|
|
104
|
+
) -> list[Path]:
|
|
105
|
+
"""cwd 로 인코딩된 세션 중 창 안에 갱신이 있는 updates.jsonl."""
|
|
106
|
+
if not started_at or not ended_at:
|
|
107
|
+
return []
|
|
108
|
+
root = session_root or grok_sessions_root()
|
|
109
|
+
if not root.is_dir():
|
|
110
|
+
return []
|
|
111
|
+
target = str(cwd)
|
|
112
|
+
matches: list[Path] = []
|
|
113
|
+
for child in root.iterdir():
|
|
114
|
+
if not child.is_dir():
|
|
115
|
+
continue
|
|
116
|
+
decoded = unquote(child.name)
|
|
117
|
+
if decoded != target:
|
|
118
|
+
continue
|
|
119
|
+
for session in child.iterdir():
|
|
120
|
+
updates = session / "updates.jsonl"
|
|
121
|
+
if updates.is_file() and _session_in_window(updates, started_at, ended_at):
|
|
122
|
+
matches.append(updates)
|
|
123
|
+
return sorted(matches)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def grok_session_dir_name(cwd: Path) -> str:
|
|
127
|
+
return quote(str(cwd), safe="")
|
|
@@ -8943,6 +8943,7 @@
|
|
|
8943
8943
|
],
|
|
8944
8944
|
"additionalProperties": false,
|
|
8945
8945
|
"properties": {
|
|
8946
|
+
"stageRefs": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "integer", "minimum": 1}},
|
|
8946
8947
|
"id": {
|
|
8947
8948
|
"type": "string",
|
|
8948
8949
|
"pattern": "^VC-\\d{3,}$"
|
|
@@ -9142,6 +9143,14 @@
|
|
|
9142
9143
|
],
|
|
9143
9144
|
"additionalProperties": false,
|
|
9144
9145
|
"properties": {
|
|
9146
|
+
"setAside": {"type": "array", "items": {"type": "object", "required": ["id", "reason"], "additionalProperties": false, "properties": {"id": {"type": "string", "minLength": 1}, "reason": {"type": "string", "enum": ["record", "observed", "deferred"]}}}},
|
|
9147
|
+
"stageLedger": {"type": "object", "additionalProperties": {"type": "string", "enum": ["done", "active", "ready", "blocked"]}},
|
|
9148
|
+
"dispatchQueue": {
|
|
9149
|
+
"type": "array",
|
|
9150
|
+
"description": "Plan-item ids sent to verifiers this round. In-scope plus plan-wide; deferred and observed stages are omitted.",
|
|
9151
|
+
"items": {"type": "string", "minLength": 1},
|
|
9152
|
+
"uniqueItems": true
|
|
9153
|
+
},
|
|
9145
9154
|
"uniformVerifiers": {
|
|
9146
9155
|
"type": "array",
|
|
9147
9156
|
"description": "Verifiers whose every vote this round was one verdict. Advisory: a unanimous round is legitimate, but the gate reads as a three-way cross-check unless this sits beside it.",
|
|
@@ -9169,6 +9178,10 @@
|
|
|
9169
9178
|
}
|
|
9170
9179
|
}
|
|
9171
9180
|
},
|
|
9181
|
+
"gating": {
|
|
9182
|
+
"type": "boolean",
|
|
9183
|
+
"description": "If false, plan-body verification is advisory: majority-disagree does not block approval, the self-fix loop does not run, and only one automatic round is allowed. Legal only when designPreparation.mode is no-design-inputs and the Stage Map has exactly one row. Prepare emits true; plan-items prepare/seed flip it after those facts exist."
|
|
9184
|
+
},
|
|
9172
9185
|
"roundCount": {
|
|
9173
9186
|
"type": "integer",
|
|
9174
9187
|
"minimum": 0
|
|
@@ -9242,6 +9255,10 @@
|
|
|
9242
9255
|
],
|
|
9243
9256
|
"additionalProperties": false,
|
|
9244
9257
|
"properties": {
|
|
9258
|
+
"block": {"type": "string", "enum": ["execution", "record"]},
|
|
9259
|
+
"stageScope": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "integer", "minimum": 1}},
|
|
9260
|
+
"contentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "SHA-256 of subject+payload+block+stageScope for the current extract."},
|
|
9261
|
+
"verifiedContentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "contentHash at the last apply-verdicts. Matching hashes keep a pre-self-fix verdict."},
|
|
9245
9262
|
"id": {
|
|
9246
9263
|
"type": "string",
|
|
9247
9264
|
"minLength": 1
|
|
@@ -9280,6 +9297,9 @@
|
|
|
9280
9297
|
],
|
|
9281
9298
|
"additionalProperties": false,
|
|
9282
9299
|
"properties": {
|
|
9300
|
+
"claimKind": {"description": "Set only on DISAGREE. `fact` claims are settled by reproduction, `judgement` by quorum.", "enum": ["fact", "judgement", null]},
|
|
9301
|
+
"reproduction": {"description": "Set only on a `fact` claim. The probe okstra runs; see okstra_ctl/claim_reproduction.py.", "type": ["object", "null"]},
|
|
9302
|
+
"reproductionResult": {"description": "Written by okstra, not by the worker.", "enum": ["reproduced", "not-reproduced", "not-runnable", null]},
|
|
9283
9303
|
"worker": {
|
|
9284
9304
|
"type": "string",
|
|
9285
9305
|
"minLength": 1
|
|
@@ -9752,6 +9772,7 @@
|
|
|
9752
9772
|
],
|
|
9753
9773
|
"additionalProperties": false,
|
|
9754
9774
|
"properties": {
|
|
9775
|
+
"stageRefs": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "integer", "minimum": 1}},
|
|
9755
9776
|
"id": {
|
|
9756
9777
|
"type": "string",
|
|
9757
9778
|
"pattern": "^R-\\d{3,}$"
|
|
@@ -8938,6 +8938,7 @@
|
|
|
8938
8938
|
],
|
|
8939
8939
|
"additionalProperties": false,
|
|
8940
8940
|
"properties": {
|
|
8941
|
+
"stageRefs": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "integer", "minimum": 1}},
|
|
8941
8942
|
"id": {
|
|
8942
8943
|
"type": "string",
|
|
8943
8944
|
"pattern": "^VC-\\d{3,}$"
|
|
@@ -9137,6 +9138,14 @@
|
|
|
9137
9138
|
],
|
|
9138
9139
|
"additionalProperties": false,
|
|
9139
9140
|
"properties": {
|
|
9141
|
+
"setAside": {"type": "array", "items": {"type": "object", "required": ["id", "reason"], "additionalProperties": false, "properties": {"id": {"type": "string", "minLength": 1}, "reason": {"type": "string", "enum": ["record", "observed", "deferred"]}}}},
|
|
9142
|
+
"stageLedger": {"type": "object", "additionalProperties": {"type": "string", "enum": ["done", "active", "ready", "blocked"]}},
|
|
9143
|
+
"dispatchQueue": {
|
|
9144
|
+
"type": "array",
|
|
9145
|
+
"description": "Plan-item ids sent to verifiers this round. In-scope plus plan-wide; deferred and observed stages are omitted.",
|
|
9146
|
+
"items": {"type": "string", "minLength": 1},
|
|
9147
|
+
"uniqueItems": true
|
|
9148
|
+
},
|
|
9140
9149
|
"uniformVerifiers": {
|
|
9141
9150
|
"type": "array",
|
|
9142
9151
|
"description": "Verifiers whose every vote this round was one verdict. Advisory: a unanimous round is legitimate, but the gate reads as a three-way cross-check unless this sits beside it.",
|
|
@@ -9164,6 +9173,10 @@
|
|
|
9164
9173
|
}
|
|
9165
9174
|
}
|
|
9166
9175
|
},
|
|
9176
|
+
"gating": {
|
|
9177
|
+
"type": "boolean",
|
|
9178
|
+
"description": "If false, plan-body verification is advisory: majority-disagree does not block approval, the self-fix loop does not run, and only one automatic round is allowed. Legal only when designPreparation.mode is no-design-inputs and the Stage Map has exactly one row. Prepare emits true; plan-items prepare/seed flip it after those facts exist."
|
|
9179
|
+
},
|
|
9167
9180
|
"roundCount": {
|
|
9168
9181
|
"type": "integer",
|
|
9169
9182
|
"minimum": 0
|
|
@@ -9237,6 +9250,10 @@
|
|
|
9237
9250
|
],
|
|
9238
9251
|
"additionalProperties": false,
|
|
9239
9252
|
"properties": {
|
|
9253
|
+
"block": {"type": "string", "enum": ["execution", "record"]},
|
|
9254
|
+
"stageScope": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "integer", "minimum": 1}},
|
|
9255
|
+
"contentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "SHA-256 of subject+payload+block+stageScope for the current extract."},
|
|
9256
|
+
"verifiedContentHash": {"type": "string", "pattern": "^[a-f0-9]{64}$", "description": "contentHash at the last apply-verdicts. Matching hashes keep a pre-self-fix verdict."},
|
|
9240
9257
|
"id": {
|
|
9241
9258
|
"type": "string",
|
|
9242
9259
|
"minLength": 1
|
|
@@ -9272,6 +9289,9 @@
|
|
|
9272
9289
|
],
|
|
9273
9290
|
"additionalProperties": false,
|
|
9274
9291
|
"properties": {
|
|
9292
|
+
"claimKind": {"description": "Set only on DISAGREE. `fact` claims are settled by reproduction, `judgement` by quorum.", "enum": ["fact", "judgement", null]},
|
|
9293
|
+
"reproduction": {"description": "Set only on a `fact` claim. The probe okstra runs; see okstra_ctl/claim_reproduction.py.", "type": ["object", "null"]},
|
|
9294
|
+
"reproductionResult": {"description": "Written by okstra, not by the worker.", "enum": ["reproduced", "not-reproduced", "not-runnable", null]},
|
|
9275
9295
|
"worker": {
|
|
9276
9296
|
"type": "string",
|
|
9277
9297
|
"minLength": 1
|
|
@@ -9744,6 +9764,7 @@
|
|
|
9744
9764
|
],
|
|
9745
9765
|
"additionalProperties": false,
|
|
9746
9766
|
"properties": {
|
|
9767
|
+
"stageRefs": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "integer", "minimum": 1}},
|
|
9747
9768
|
"id": {
|
|
9748
9769
|
"type": "string",
|
|
9749
9770
|
"pattern": "^R-\\d{3,}$"
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://okstra.dev/schemas/report-synthesis-packet-v1.0.schema.json",
|
|
4
|
+
"title": "Okstra report synthesis packet",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"schemaVersion",
|
|
8
|
+
"taskKey",
|
|
9
|
+
"taskType",
|
|
10
|
+
"authoringContract",
|
|
11
|
+
"accountingSnapshot",
|
|
12
|
+
"sources"
|
|
13
|
+
],
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"properties": {
|
|
16
|
+
"schemaVersion": {
|
|
17
|
+
"const": "1.0"
|
|
18
|
+
},
|
|
19
|
+
"taskKey": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"minLength": 1
|
|
22
|
+
},
|
|
23
|
+
"taskType": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 1
|
|
26
|
+
},
|
|
27
|
+
"authoringContract": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"required": [
|
|
30
|
+
"resultPath",
|
|
31
|
+
"format",
|
|
32
|
+
"sourcePolicy",
|
|
33
|
+
"instructions",
|
|
34
|
+
"runtimeOwnedContent",
|
|
35
|
+
"validationRules"
|
|
36
|
+
],
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"properties": {
|
|
39
|
+
"resultPath": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"minLength": 1
|
|
42
|
+
},
|
|
43
|
+
"format": {
|
|
44
|
+
"const": "report-narrative-v3.0"
|
|
45
|
+
},
|
|
46
|
+
"sourcePolicy": {
|
|
47
|
+
"const": "read-only-synthesis-packet"
|
|
48
|
+
},
|
|
49
|
+
"instructions": {
|
|
50
|
+
"type": "array",
|
|
51
|
+
"minItems": 1,
|
|
52
|
+
"uniqueItems": true,
|
|
53
|
+
"items": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"minLength": 1
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"runtimeOwnedContent": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"minItems": 1,
|
|
61
|
+
"uniqueItems": true,
|
|
62
|
+
"items": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"validationRules": {
|
|
68
|
+
"type": "array",
|
|
69
|
+
"minItems": 1,
|
|
70
|
+
"uniqueItems": true,
|
|
71
|
+
"items": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"minLength": 1
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"accountingSnapshot": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"required": [
|
|
81
|
+
"leadSessionIds",
|
|
82
|
+
"leadUsage",
|
|
83
|
+
"workerUsage",
|
|
84
|
+
"usageSummary"
|
|
85
|
+
],
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"properties": {
|
|
88
|
+
"leadSessionIds": {
|
|
89
|
+
"type": "array",
|
|
90
|
+
"items": {"type": "string"}
|
|
91
|
+
},
|
|
92
|
+
"leadUsage": {"type": "object"},
|
|
93
|
+
"workerUsage": {
|
|
94
|
+
"type": "array",
|
|
95
|
+
"items": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"required": ["workerId", "role", "usage"],
|
|
98
|
+
"additionalProperties": false,
|
|
99
|
+
"properties": {
|
|
100
|
+
"workerId": {"type": "string", "minLength": 1},
|
|
101
|
+
"role": {"type": "string"},
|
|
102
|
+
"usage": {"type": "object"}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"usageSummary": {"type": "object"}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"sources": {
|
|
110
|
+
"type": "array",
|
|
111
|
+
"minItems": 1,
|
|
112
|
+
"items": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"required": ["label", "owner", "path", "digest", "content"],
|
|
115
|
+
"additionalProperties": false,
|
|
116
|
+
"properties": {
|
|
117
|
+
"label": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
"minLength": 1
|
|
120
|
+
},
|
|
121
|
+
"owner": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"minLength": 1
|
|
124
|
+
},
|
|
125
|
+
"path": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"minLength": 1
|
|
128
|
+
},
|
|
129
|
+
"digest": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
132
|
+
},
|
|
133
|
+
"content": {
|
|
134
|
+
"type": "string"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -120,10 +120,10 @@ Run one Bash tool call, starting with the literal token `okstra` (never wrapped
|
|
|
120
120
|
<!-- END FRAGMENT: bash-invocation-rule -->
|
|
121
121
|
|
|
122
122
|
```bash
|
|
123
|
-
okstra preflight --runtime claude-code
|
|
123
|
+
okstra preflight --runtime claude-code
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
On `Okstra preflight: ready`, carry the fixed `Project root` line as a literal string into the steps below. On `Okstra preflight: failed` (or `okstra` not on `PATH` at all), show the fixed `Reason` and `Recovery` lines, then stop.
|
|
127
127
|
|
|
128
128
|
<!-- BEGIN FRAGMENT: preflight-outdated-cli -->
|
|
129
129
|
If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
|
|
@@ -328,16 +328,18 @@ command is its own Bash call starting with the literal token `okstra`, per
|
|
|
328
328
|
Step 0):
|
|
329
329
|
|
|
330
330
|
```bash
|
|
331
|
-
okstra task-list
|
|
331
|
+
okstra task-list --text
|
|
332
332
|
```
|
|
333
333
|
|
|
334
|
-
|
|
335
|
-
name, not a slug** — `slugify` each value (lowercase +
|
|
336
|
-
`-`) and **dedupe by slug** so case/format variants collapse (e.g.
|
|
334
|
+
Read each fixed `Task group` and `Updated at` line. `Task group` is a **raw
|
|
335
|
+
display name, not a slug** — `slugify` each value (lowercase +
|
|
336
|
+
non-alphanumeric → `-`) and **dedupe by slug** so case/format variants collapse (e.g.
|
|
337
337
|
`uploadFont` and `uploadfont` are one group, not two). Order distinct slugs
|
|
338
338
|
by most-recent `updatedAt` and take the **2 most-recent** as recommendations,
|
|
339
339
|
shown in slug form.
|
|
340
340
|
|
|
341
|
+
If `Status` is `error`, report `Failure stage` and `Failure reason`, then stop.
|
|
342
|
+
|
|
341
343
|
`AskUserQuestion` (single-select):
|
|
342
344
|
|
|
343
345
|
- **Label**: `"Task group?"`
|
|
@@ -349,7 +351,7 @@ shown in slug form.
|
|
|
349
351
|
When the user picks an existing group, use its value verbatim. When they
|
|
350
352
|
pick `Enter directly`, take a free-text value via a follow-up question.
|
|
351
353
|
|
|
352
|
-
If `okstra task-list` returns
|
|
354
|
+
If `okstra task-list --text` returns `Task count: 0` (no prior tasks), skip the
|
|
353
355
|
picker and ask one free-text question:
|
|
354
356
|
`"Task group (e.g. backend-api, INV-1234, refactor)"` → `task_group`.
|
|
355
357
|
|