okstra 0.174.0 → 0.176.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.
Files changed (419) hide show
  1. package/README.md +18 -9
  2. package/bin/okstra +8 -4
  3. package/dist/cli-registry.d.mts +16 -0
  4. package/dist/cli-registry.mjs +560 -0
  5. package/dist/cli-registry.mjs.map +1 -0
  6. package/dist/commands/execute/agent-prompt.d.mts +1 -0
  7. package/{src → dist}/commands/execute/agent-prompt.mjs +10 -11
  8. package/dist/commands/execute/agent-prompt.mjs.map +1 -0
  9. package/dist/commands/execute/codex-dispatch.d.mts +3 -0
  10. package/dist/commands/execute/codex-dispatch.mjs +6 -0
  11. package/dist/commands/execute/codex-dispatch.mjs.map +1 -0
  12. package/dist/commands/execute/codex-run.d.mts +3 -0
  13. package/{src → dist}/commands/execute/codex-run.mjs +30 -37
  14. package/dist/commands/execute/codex-run.mjs.map +1 -0
  15. package/dist/commands/execute/convergence.d.mts +1 -0
  16. package/{src → dist}/commands/execute/convergence.mjs +15 -16
  17. package/dist/commands/execute/convergence.mjs.map +1 -0
  18. package/dist/commands/execute/error-log.d.mts +1 -0
  19. package/{src → dist}/commands/execute/error-log.mjs +2 -3
  20. package/dist/commands/execute/error-log.mjs.map +1 -0
  21. package/dist/commands/execute/git-reconcile.d.mts +1 -0
  22. package/{src → dist}/commands/execute/git-reconcile.mjs +10 -11
  23. package/dist/commands/execute/git-reconcile.mjs.map +1 -0
  24. package/dist/commands/execute/handoff.d.mts +1 -0
  25. package/{src → dist}/commands/execute/handoff.mjs +10 -11
  26. package/dist/commands/execute/handoff.mjs.map +1 -0
  27. package/dist/commands/execute/incremental-carry.d.mts +1 -0
  28. package/{src → dist}/commands/execute/incremental-carry.mjs +2 -3
  29. package/dist/commands/execute/incremental-carry.mjs.map +1 -0
  30. package/dist/commands/execute/incremental-scope.d.mts +1 -0
  31. package/{src → dist}/commands/execute/incremental-scope.mjs +2 -3
  32. package/dist/commands/execute/incremental-scope.mjs.map +1 -0
  33. package/dist/commands/execute/integrate-stages.d.mts +1 -0
  34. package/{src → dist}/commands/execute/integrate-stages.mjs +10 -11
  35. package/dist/commands/execute/integrate-stages.mjs.map +1 -0
  36. package/dist/commands/execute/pane-title.d.mts +1 -0
  37. package/dist/commands/execute/pane-title.mjs +20 -0
  38. package/dist/commands/execute/pane-title.mjs.map +1 -0
  39. package/dist/commands/execute/plan-items.d.mts +1 -0
  40. package/dist/commands/execute/plan-items.mjs +9 -0
  41. package/dist/commands/execute/plan-items.mjs.map +1 -0
  42. package/dist/commands/execute/plan-validate.d.mts +1 -0
  43. package/dist/commands/execute/plan-validate.mjs +68 -0
  44. package/dist/commands/execute/plan-validate.mjs.map +1 -0
  45. package/dist/commands/execute/plan-verify.d.mts +1 -0
  46. package/{src → dist}/commands/execute/plan-verify.mjs +13 -16
  47. package/dist/commands/execute/plan-verify.mjs.map +1 -0
  48. package/dist/commands/execute/render-bundle.d.mts +3 -0
  49. package/{src → dist}/commands/execute/render-bundle.mjs +65 -61
  50. package/dist/commands/execute/render-bundle.mjs.map +1 -0
  51. package/dist/commands/execute/run.d.mts +24 -0
  52. package/dist/commands/execute/run.mjs +196 -0
  53. package/dist/commands/execute/run.mjs.map +1 -0
  54. package/dist/commands/execute/spawn-followups.d.mts +1 -0
  55. package/{src → dist}/commands/execute/spawn-followups.mjs +6 -7
  56. package/dist/commands/execute/spawn-followups.mjs.map +1 -0
  57. package/dist/commands/execute/team.d.mts +3 -0
  58. package/{src → dist}/commands/execute/team.mjs +29 -36
  59. package/dist/commands/execute/team.mjs.map +1 -0
  60. package/dist/commands/execute/token-usage.d.mts +1 -0
  61. package/{src → dist}/commands/execute/token-usage.mjs +2 -3
  62. package/dist/commands/execute/token-usage.mjs.map +1 -0
  63. package/dist/commands/execute/wizard.d.mts +8 -0
  64. package/dist/commands/execute/wizard.mjs +191 -0
  65. package/dist/commands/execute/wizard.mjs.map +1 -0
  66. package/dist/commands/execute/worker-audit-check.d.mts +1 -0
  67. package/{src → dist}/commands/execute/worker-audit-check.mjs +11 -12
  68. package/dist/commands/execute/worker-audit-check.mjs.map +1 -0
  69. package/dist/commands/execute/worker-dispatch.d.mts +7 -0
  70. package/{src → dist}/commands/execute/worker-dispatch.mjs +34 -46
  71. package/dist/commands/execute/worker-dispatch.mjs.map +1 -0
  72. package/dist/commands/execute/worker-state.d.mts +1 -0
  73. package/{src → dist}/commands/execute/worker-state.mjs +15 -16
  74. package/dist/commands/execute/worker-state.mjs.map +1 -0
  75. package/dist/commands/execute/worktree-lookup.d.mts +1 -0
  76. package/{src → dist}/commands/execute/worktree-lookup.mjs +38 -37
  77. package/dist/commands/execute/worktree-lookup.mjs.map +1 -0
  78. package/dist/commands/execute/worktree-status.d.mts +1 -0
  79. package/{src → dist}/commands/execute/worktree-status.mjs +52 -46
  80. package/dist/commands/execute/worktree-status.mjs.map +1 -0
  81. package/dist/commands/inspect/code-review.d.mts +1 -0
  82. package/{src → dist}/commands/inspect/code-review.mjs +16 -19
  83. package/dist/commands/inspect/code-review.mjs.map +1 -0
  84. package/dist/commands/inspect/container.d.mts +1 -0
  85. package/{src → dist}/commands/inspect/container.mjs +11 -13
  86. package/dist/commands/inspect/container.mjs.map +1 -0
  87. package/dist/commands/inspect/context-cost.d.mts +1 -0
  88. package/{src → dist}/commands/inspect/context-cost.mjs +11 -13
  89. package/dist/commands/inspect/context-cost.mjs.map +1 -0
  90. package/dist/commands/inspect/design-prep.d.mts +1 -0
  91. package/{src → dist}/commands/inspect/design-prep.mjs +10 -11
  92. package/dist/commands/inspect/design-prep.mjs.map +1 -0
  93. package/dist/commands/inspect/error-report.d.mts +1 -0
  94. package/{src → dist}/commands/inspect/error-report.mjs +11 -13
  95. package/dist/commands/inspect/error-report.mjs.map +1 -0
  96. package/dist/commands/inspect/error-zip.d.mts +1 -0
  97. package/{src → dist}/commands/inspect/error-zip.mjs +11 -12
  98. package/dist/commands/inspect/error-zip.mjs.map +1 -0
  99. package/dist/commands/inspect/log-report.d.mts +1 -0
  100. package/{src → dist}/commands/inspect/log-report.mjs +11 -13
  101. package/dist/commands/inspect/log-report.mjs.map +1 -0
  102. package/dist/commands/inspect/profile-show.d.mts +1 -0
  103. package/{src → dist}/commands/inspect/profile-show.mjs +10 -11
  104. package/dist/commands/inspect/profile-show.mjs.map +1 -0
  105. package/dist/commands/inspect/recap.d.mts +1 -0
  106. package/{src → dist}/commands/inspect/recap.mjs +11 -13
  107. package/dist/commands/inspect/recap.mjs.map +1 -0
  108. package/dist/commands/inspect/resolve-task-key.d.mts +1 -0
  109. package/{src → dist}/commands/inspect/resolve-task-key.mjs +11 -13
  110. package/dist/commands/inspect/resolve-task-key.mjs.map +1 -0
  111. package/dist/commands/inspect/rollup.d.mts +1 -0
  112. package/{src → dist}/commands/inspect/rollup.mjs +11 -13
  113. package/dist/commands/inspect/rollup.mjs.map +1 -0
  114. package/dist/commands/inspect/run-audit.d.mts +1 -0
  115. package/{src → dist}/commands/inspect/run-audit.mjs +11 -12
  116. package/dist/commands/inspect/run-audit.mjs.map +1 -0
  117. package/dist/commands/inspect/set-work-status.d.mts +1 -0
  118. package/{src → dist}/commands/inspect/set-work-status.mjs +11 -13
  119. package/dist/commands/inspect/set-work-status.mjs.map +1 -0
  120. package/dist/commands/inspect/stage-map.d.mts +1 -0
  121. package/dist/commands/inspect/stage-map.mjs +110 -0
  122. package/dist/commands/inspect/stage-map.mjs.map +1 -0
  123. package/dist/commands/inspect/task-list.d.mts +1 -0
  124. package/dist/commands/inspect/task-list.mjs +103 -0
  125. package/dist/commands/inspect/task-list.mjs.map +1 -0
  126. package/dist/commands/inspect/task-show.d.mts +1 -0
  127. package/dist/commands/inspect/task-show.mjs +108 -0
  128. package/dist/commands/inspect/task-show.mjs.map +1 -0
  129. package/dist/commands/inspect/time-report.d.mts +1 -0
  130. package/{src → dist}/commands/inspect/time-report.mjs +11 -13
  131. package/dist/commands/inspect/time-report.mjs.map +1 -0
  132. package/dist/commands/inspect/usage-report.d.mts +1 -0
  133. package/{src → dist}/commands/inspect/usage-report.mjs +11 -13
  134. package/dist/commands/inspect/usage-report.mjs.map +1 -0
  135. package/dist/commands/inspect/user-response.d.mts +1 -0
  136. package/{src → dist}/commands/inspect/user-response.mjs +10 -11
  137. package/dist/commands/inspect/user-response.mjs.map +1 -0
  138. package/dist/commands/inspect/worker-liveness.d.mts +1 -0
  139. package/{src → dist}/commands/inspect/worker-liveness.mjs +11 -13
  140. package/dist/commands/inspect/worker-liveness.mjs.map +1 -0
  141. package/dist/commands/lifecycle/check-project.d.mts +21 -0
  142. package/dist/commands/lifecycle/check-project.mjs +175 -0
  143. package/dist/commands/lifecycle/check-project.mjs.map +1 -0
  144. package/dist/commands/lifecycle/config.d.mts +1 -0
  145. package/dist/commands/lifecycle/config.mjs +369 -0
  146. package/dist/commands/lifecycle/config.mjs.map +1 -0
  147. package/dist/commands/lifecycle/doctor.d.mts +10 -0
  148. package/dist/commands/lifecycle/doctor.mjs +317 -0
  149. package/dist/commands/lifecycle/doctor.mjs.map +1 -0
  150. package/dist/commands/lifecycle/install.d.mts +17 -0
  151. package/dist/commands/lifecycle/install.mjs +1077 -0
  152. package/dist/commands/lifecycle/install.mjs.map +1 -0
  153. package/dist/commands/lifecycle/migrate.d.mts +1 -0
  154. package/{src → dist}/commands/lifecycle/migrate.mjs +10 -11
  155. package/dist/commands/lifecycle/migrate.mjs.map +1 -0
  156. package/dist/commands/lifecycle/model.d.mts +1 -0
  157. package/dist/commands/lifecycle/model.mjs +22 -0
  158. package/dist/commands/lifecycle/model.mjs.map +1 -0
  159. package/dist/commands/lifecycle/paths.d.mts +1 -0
  160. package/dist/commands/lifecycle/paths.mjs +102 -0
  161. package/dist/commands/lifecycle/paths.mjs.map +1 -0
  162. package/dist/commands/lifecycle/preflight.d.mts +1 -0
  163. package/dist/commands/lifecycle/preflight.mjs +124 -0
  164. package/dist/commands/lifecycle/preflight.mjs.map +1 -0
  165. package/dist/commands/lifecycle/setup.d.mts +1 -0
  166. package/dist/commands/lifecycle/setup.mjs +297 -0
  167. package/dist/commands/lifecycle/setup.mjs.map +1 -0
  168. package/dist/commands/lifecycle/uninstall.d.mts +4 -0
  169. package/dist/commands/lifecycle/uninstall.mjs +262 -0
  170. package/dist/commands/lifecycle/uninstall.mjs.map +1 -0
  171. package/dist/commands/manager.d.mts +3 -0
  172. package/{src → dist}/commands/manager.mjs +25 -32
  173. package/dist/commands/manager.mjs.map +1 -0
  174. package/dist/commands/memory/memory.d.mts +1 -0
  175. package/dist/commands/memory/memory.mjs +569 -0
  176. package/dist/commands/memory/memory.mjs.map +1 -0
  177. package/dist/commands/pr/pr.d.mts +33 -0
  178. package/dist/commands/pr/pr.mjs +296 -0
  179. package/dist/commands/pr/pr.mjs.map +1 -0
  180. package/dist/commands/report/agent-activity.d.mts +1 -0
  181. package/{src → dist}/commands/report/agent-activity.mjs +11 -12
  182. package/dist/commands/report/agent-activity.mjs.map +1 -0
  183. package/dist/commands/report/finalize.d.mts +4 -0
  184. package/{src → dist}/commands/report/finalize.mjs +25 -32
  185. package/dist/commands/report/finalize.mjs.map +1 -0
  186. package/dist/commands/report/inject-report-index.d.mts +1 -0
  187. package/{src → dist}/commands/report/inject-report-index.mjs +6 -7
  188. package/dist/commands/report/inject-report-index.mjs.map +1 -0
  189. package/dist/commands/report/render-final-report.d.mts +1 -0
  190. package/{src → dist}/commands/report/render-final-report.mjs +6 -7
  191. package/dist/commands/report/render-final-report.mjs.map +1 -0
  192. package/dist/commands/report/render-views.d.mts +1 -0
  193. package/{src → dist}/commands/report/render-views.mjs +6 -7
  194. package/dist/commands/report/render-views.mjs.map +1 -0
  195. package/dist/commands/report/translate.d.mts +1 -0
  196. package/{src → dist}/commands/report/translate.mjs +6 -7
  197. package/dist/commands/report/translate.mjs.map +1 -0
  198. package/dist/lib/helper-scripts.d.mts +3 -0
  199. package/{src → dist}/lib/helper-scripts.mjs +11 -14
  200. package/dist/lib/helper-scripts.mjs.map +1 -0
  201. package/dist/lib/host-registry-client.d.mts +25 -0
  202. package/dist/lib/host-registry-client.mjs +152 -0
  203. package/dist/lib/host-registry-client.mjs.map +1 -0
  204. package/dist/lib/install-assets.d.mts +8 -0
  205. package/{src → dist}/lib/install-assets.mjs +17 -20
  206. package/dist/lib/install-assets.mjs.map +1 -0
  207. package/dist/lib/okstra-dirs.d.mts +4 -0
  208. package/{src → dist}/lib/okstra-dirs.mjs +3 -6
  209. package/dist/lib/okstra-dirs.mjs.map +1 -0
  210. package/dist/lib/paths.d.mts +4 -0
  211. package/dist/lib/paths.mjs +78 -0
  212. package/dist/lib/paths.mjs.map +1 -0
  213. package/dist/lib/proc.d.mts +3 -0
  214. package/dist/lib/proc.mjs +31 -0
  215. package/dist/lib/proc.mjs.map +1 -0
  216. package/dist/lib/python-helper.d.mts +8 -0
  217. package/dist/lib/python-helper.mjs +123 -0
  218. package/dist/lib/python-helper.mjs.map +1 -0
  219. package/dist/lib/runtime-manifest.d.mts +7 -0
  220. package/dist/lib/runtime-manifest.mjs +73 -0
  221. package/dist/lib/runtime-manifest.mjs.map +1 -0
  222. package/dist/lib/skill-catalog.d.mts +4 -0
  223. package/dist/lib/skill-catalog.mjs +50 -0
  224. package/dist/lib/skill-catalog.mjs.map +1 -0
  225. package/dist/lib/types.d.mts +129 -0
  226. package/dist/lib/types.mjs +2 -0
  227. package/dist/lib/types.mjs.map +1 -0
  228. package/dist/lib/version.d.mts +2 -0
  229. package/dist/lib/version.mjs +27 -0
  230. package/dist/lib/version.mjs.map +1 -0
  231. package/docs/architecture/storage-model.md +6 -2
  232. package/docs/architecture.md +29 -20
  233. package/docs/cli.md +40 -16
  234. package/docs/contributor-change-matrix.md +2 -2
  235. package/docs/for-ai/skills/okstra-inspect.md +2 -3
  236. package/docs/for-ai/skills/okstra-rollup.md +1 -0
  237. package/docs/project-structure-overview.md +72 -62
  238. package/docs/task-process/README.md +12 -8
  239. package/docs/task-process/common-flow.md +13 -16
  240. package/docs/task-process/error-analysis.md +9 -10
  241. package/docs/task-process/final-verification.md +7 -7
  242. package/docs/task-process/implementation-planning.md +9 -9
  243. package/docs/task-process/implementation.md +6 -6
  244. package/docs/task-process/release-handoff.md +8 -7
  245. package/docs/task-process/requirements-discovery.md +8 -8
  246. package/package.json +11 -5
  247. package/runtime/BUILD.json +2 -2
  248. package/runtime/agents/workers/claude-worker.md +2 -1
  249. package/runtime/agents/workers/report-writer-worker.md +3 -1
  250. package/runtime/agents/workers/translator-worker.md +3 -1
  251. package/runtime/bin/lib/okstra/cli.sh +12 -0
  252. package/runtime/bin/lib/okstra/globals.sh +3 -0
  253. package/runtime/bin/lib/okstra/interactive.sh +12 -6
  254. package/runtime/bin/lib/okstra/usage.sh +7 -6
  255. package/runtime/bin/okstra-error-log.py +27 -0
  256. package/runtime/bin/okstra-provider-exec.py +217 -5
  257. package/runtime/bin/okstra-spawn-followups.py +4 -2
  258. package/runtime/bin/okstra.sh +10 -0
  259. package/runtime/prompts/launch.template.md +2 -2
  260. package/runtime/prompts/lead/context-loader.md +1 -2
  261. package/runtime/prompts/lead/convergence.md +27 -18
  262. package/runtime/prompts/lead/okstra-lead-contract.md +6 -5
  263. package/runtime/prompts/lead/plan-body-verification.md +10 -3
  264. package/runtime/prompts/lead/report-writer.md +15 -1
  265. package/runtime/prompts/lead/team-contract.md +17 -13
  266. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  267. package/runtime/prompts/profiles/_implementation-executor.md +0 -3
  268. package/runtime/prompts/profiles/_implementation-verifier.md +3 -7
  269. package/runtime/prompts/profiles/change-impact-analysis.md +20 -0
  270. package/runtime/prompts/profiles/error-analysis.md +27 -1
  271. package/runtime/prompts/profiles/feature-analysis.md +20 -0
  272. package/runtime/prompts/profiles/final-verification.md +20 -1
  273. package/runtime/prompts/profiles/implementation-option-selection.md +20 -0
  274. package/runtime/prompts/profiles/implementation-planning.md +26 -0
  275. package/runtime/prompts/profiles/implementation.md +19 -0
  276. package/runtime/prompts/profiles/improvement-discovery.md +21 -1
  277. package/runtime/prompts/profiles/project-analysis.md +20 -0
  278. package/runtime/prompts/profiles/release-handoff.md +4 -0
  279. package/runtime/prompts/profiles/requirements-discovery.md +27 -1
  280. package/runtime/prompts/wizard/prompts.ko.json +35 -0
  281. package/runtime/python/okstra_ctl/adapters/dispatch/cli_wrapper.py +8 -0
  282. package/runtime/python/okstra_ctl/adapters/dispatch/cmux.py +8 -0
  283. package/runtime/python/okstra_ctl/adapters/dispatch/native_team.py +13 -0
  284. package/runtime/python/okstra_ctl/adapters/hosts/antigravity/adapter.py +2 -2
  285. package/runtime/python/okstra_ctl/adapters/hosts/claude-code/adapter.py +25 -5
  286. package/runtime/python/okstra_ctl/adapters/hosts/codex/adapter.py +2 -2
  287. package/runtime/python/okstra_ctl/adapters/hosts/external/adapter.py +4 -1
  288. package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -2
  289. package/runtime/python/okstra_ctl/adapters/hosts/kimi/adapter.py +2 -2
  290. package/runtime/python/okstra_ctl/adapters/providers/antigravity/adapter.py +79 -11
  291. package/runtime/python/okstra_ctl/adapters/providers/claude/adapter.py +95 -25
  292. package/runtime/python/okstra_ctl/adapters/providers/codex/adapter.py +59 -14
  293. package/runtime/python/okstra_ctl/adapters/providers/grok/adapter.py +55 -9
  294. package/runtime/python/okstra_ctl/adapters/providers/kimi/adapter.py +51 -12
  295. package/runtime/python/okstra_ctl/adapters/runtime/__init__.py +1 -0
  296. package/runtime/python/okstra_ctl/adapters/runtime/assembly.py +27 -0
  297. package/runtime/python/okstra_ctl/adapters/runtime/cli_wrapper.py +49 -0
  298. package/runtime/python/okstra_ctl/adapters/runtime/cmux.py +74 -0
  299. package/runtime/python/okstra_ctl/agent_activity.py +5 -1
  300. package/runtime/python/okstra_ctl/agent_invocation.py +608 -14
  301. package/runtime/python/okstra_ctl/agent_prompt_cli.py +249 -50
  302. package/runtime/python/okstra_ctl/application/open_worker.py +29 -0
  303. package/runtime/python/okstra_ctl/application/resolve_assignment.py +53 -12
  304. package/runtime/python/okstra_ctl/assignment_environment.py +102 -0
  305. package/runtime/python/okstra_ctl/assignment_resolver.py +790 -0
  306. package/runtime/python/okstra_ctl/attempt_evidence.py +342 -0
  307. package/runtime/python/okstra_ctl/convergence.py +409 -3
  308. package/runtime/python/okstra_ctl/convergence_engine.py +248 -15
  309. package/runtime/python/okstra_ctl/convergence_store.py +170 -1
  310. package/runtime/python/okstra_ctl/dispatch_core.py +1204 -155
  311. package/runtime/python/okstra_ctl/dispatch_state.py +696 -11
  312. package/runtime/python/okstra_ctl/doctor.py +14 -0
  313. package/runtime/python/okstra_ctl/domain/host.py +34 -1
  314. package/runtime/python/okstra_ctl/domain/provider.py +212 -6
  315. package/runtime/python/okstra_ctl/domain/role.py +125 -0
  316. package/runtime/python/okstra_ctl/domain/wizard/interaction.py +18 -0
  317. package/runtime/python/okstra_ctl/domain/worker_exec.py +34 -1
  318. package/runtime/python/okstra_ctl/domain/worker_role.py +13 -3
  319. package/runtime/python/okstra_ctl/domain/worker_runtime.py +44 -0
  320. package/runtime/python/okstra_ctl/domain/worker_stream.py +24 -0
  321. package/runtime/python/okstra_ctl/error_log_write.py +9 -0
  322. package/runtime/python/okstra_ctl/error_report.py +21 -1
  323. package/runtime/python/okstra_ctl/execution_identity.py +529 -0
  324. package/runtime/python/okstra_ctl/execution_manifest.py +1281 -0
  325. package/runtime/python/okstra_ctl/execution_mutation_audit.py +619 -0
  326. package/runtime/python/okstra_ctl/implementation_outcome.py +21 -5
  327. package/runtime/python/okstra_ctl/index.py +6 -2
  328. package/runtime/python/okstra_ctl/initial_prompt_materialization.py +43 -3
  329. package/runtime/python/okstra_ctl/legacy_model_selection.py +578 -0
  330. package/runtime/python/okstra_ctl/manager_sync.py +4 -1
  331. package/runtime/python/okstra_ctl/model_cli.py +278 -0
  332. package/runtime/python/okstra_ctl/model_defaults.py +46 -0
  333. package/runtime/python/okstra_ctl/model_discovery.py +26 -7
  334. package/runtime/python/okstra_ctl/model_pool.py +501 -0
  335. package/runtime/python/okstra_ctl/models.py +47 -27
  336. package/runtime/python/okstra_ctl/mutation_recovery.py +293 -0
  337. package/runtime/python/okstra_ctl/next_phase.py +236 -0
  338. package/runtime/python/okstra_ctl/pane_title.py +151 -0
  339. package/runtime/python/okstra_ctl/ports/host_model.py +48 -17
  340. package/runtime/python/okstra_ctl/ports/worker_dispatch.py +4 -0
  341. package/runtime/python/okstra_ctl/ports/worker_runtime.py +26 -0
  342. package/runtime/python/okstra_ctl/recap.py +4 -1
  343. package/runtime/python/okstra_ctl/registry/provider_registry.py +8 -4
  344. package/runtime/python/okstra_ctl/render.py +124 -57
  345. package/runtime/python/okstra_ctl/render_final_report.py +19 -0
  346. package/runtime/python/okstra_ctl/report_contract.py +39 -0
  347. package/runtime/python/okstra_ctl/report_finalize.py +16 -0
  348. package/runtime/python/okstra_ctl/report_html/render.py +1 -0
  349. package/runtime/python/okstra_ctl/role_requirements.py +210 -0
  350. package/runtime/python/okstra_ctl/rollup.py +4 -1
  351. package/runtime/python/okstra_ctl/run.py +1210 -258
  352. package/runtime/python/okstra_ctl/run_index_row.py +7 -1
  353. package/runtime/python/okstra_ctl/stage_fix_carry.py +17 -1
  354. package/runtime/python/okstra_ctl/team.py +14 -18
  355. package/runtime/python/okstra_ctl/time_report.py +38 -4
  356. package/runtime/python/okstra_ctl/usage_identity.py +86 -0
  357. package/runtime/python/okstra_ctl/usage_report.py +15 -3
  358. package/runtime/python/okstra_ctl/wizard.py +1449 -66
  359. package/runtime/python/okstra_ctl/worker_audit_ledger.py +68 -65
  360. package/runtime/python/okstra_ctl/worker_prompt_body.py +18 -2
  361. package/runtime/python/okstra_ctl/worker_prompt_contract.py +40 -9
  362. package/runtime/python/okstra_ctl/worker_prompt_headers.py +16 -1
  363. package/runtime/python/okstra_ctl/worker_request.py +7 -1
  364. package/runtime/python/okstra_ctl/worker_runner.py +130 -5
  365. package/runtime/python/okstra_ctl/workers.py +7 -5
  366. package/runtime/python/okstra_ctl/workflow.py +18 -32
  367. package/runtime/python/okstra_ctl/worktree.py +3 -3
  368. package/runtime/python/okstra_ctl/worktree_registry.py +5 -4
  369. package/runtime/python/okstra_ctl/write_policy.py +571 -0
  370. package/runtime/python/okstra_project/state.py +54 -6
  371. package/runtime/schemas/convergence-groups-v2.0.schema.json +128 -0
  372. package/runtime/schemas/execution-manifest-v2.schema.json +340 -0
  373. package/runtime/schemas/final-report-v2.0.schema.json +68 -5
  374. package/runtime/skills/okstra-inspect/facets/recap.md +2 -0
  375. package/runtime/skills/okstra-inspect/facets/report.md +1 -1
  376. package/runtime/skills/okstra-inspect/facets/status.md +15 -13
  377. package/runtime/skills/okstra-rollup/SKILL.md +1 -0
  378. package/runtime/skills/okstra-run/SKILL.md +14 -14
  379. package/runtime/templates/implementation-worker-preamble.md +3 -18
  380. package/runtime/templates/project-docs/task-index.template.md +0 -1
  381. package/runtime/templates/report-writer-prompt-preamble.md +2 -0
  382. package/runtime/templates/reports/final-report-v2.template.md +7 -1
  383. package/runtime/templates/reports/html/base.template.html +21 -0
  384. package/runtime/templates/reports/html/i18n/en.json +4 -0
  385. package/runtime/templates/reports/html/i18n/ko.json +4 -0
  386. package/runtime/templates/reports/html/macros/forms.html +5 -4
  387. package/runtime/templates/reports/html/tasks/final-verification.template.html +1 -1
  388. package/runtime/templates/reports/html/tasks/implementation.template.html +1 -1
  389. package/runtime/templates/worker-prompt-preamble.md +6 -35
  390. package/runtime/validators/lib/fixtures.sh +49 -16
  391. package/runtime/validators/validate-run.py +486 -134
  392. package/runtime/validators/validate_session_conformance.py +25 -7
  393. package/src/cli-registry.mjs +0 -539
  394. package/src/commands/execute/codex-dispatch.mjs +0 -10
  395. package/src/commands/execute/plan-items.mjs +0 -9
  396. package/src/commands/execute/plan-validate.mjs +0 -68
  397. package/src/commands/execute/run.mjs +0 -208
  398. package/src/commands/execute/wizard.mjs +0 -167
  399. package/src/commands/inspect/stage-map.mjs +0 -98
  400. package/src/commands/inspect/task-list.mjs +0 -97
  401. package/src/commands/inspect/task-show.mjs +0 -100
  402. package/src/commands/lifecycle/check-project.mjs +0 -187
  403. package/src/commands/lifecycle/config.mjs +0 -385
  404. package/src/commands/lifecycle/doctor.mjs +0 -308
  405. package/src/commands/lifecycle/install.mjs +0 -1170
  406. package/src/commands/lifecycle/paths.mjs +0 -104
  407. package/src/commands/lifecycle/preflight.mjs +0 -125
  408. package/src/commands/lifecycle/setup.mjs +0 -333
  409. package/src/commands/lifecycle/uninstall.mjs +0 -279
  410. package/src/commands/memory/memory.mjs +0 -524
  411. package/src/commands/pr/pr.mjs +0 -261
  412. package/src/lib/host-registry-client.mjs +0 -176
  413. package/src/lib/paths.mjs +0 -86
  414. package/src/lib/proc.mjs +0 -31
  415. package/src/lib/python-helper.mjs +0 -142
  416. package/src/lib/runtime-manifest.mjs +0 -70
  417. package/src/lib/skill-catalog.mjs +0 -53
  418. package/src/lib/version.mjs +0 -20
  419. /package/{src → dist}/commands/pr/default.md +0 -0
@@ -1,261 +0,0 @@
1
- import { promises as fs } from "node:fs";
2
- import { execFileSync } from "node:child_process";
3
- import { fileURLToPath } from "node:url";
4
- import { dirname, join, resolve } from "node:path";
5
- import { createInterface } from "node:readline/promises";
6
- import { stdin as input, stdout as output } from "node:process";
7
- import { resolveOkstraHome } from "../../lib/paths.mjs";
8
-
9
- const NAME_RE = /^[A-Za-z0-9._-]+$/;
10
- const DEFAULT_TEMPLATE_PATH = join(
11
- dirname(fileURLToPath(import.meta.url)),
12
- "default.md",
13
- );
14
-
15
- const USAGE = `okstra pr — PR body templates and PR generation
16
-
17
- Usage:
18
- okstra pr template list [--json]
19
- okstra pr template show <name|default> [--json]
20
- okstra pr template add --name <name> (--content <text> | --file <path>) [--yes]
21
- okstra pr template path
22
- okstra pr branches [--json]
23
- okstra pr gen --base <ref> [--template <name|default>] [--json]
24
- `;
25
-
26
- export function isValidTemplateName(name) {
27
- return (
28
- typeof name === "string" &&
29
- NAME_RE.test(name) &&
30
- name !== "." &&
31
- name !== ".."
32
- );
33
- }
34
-
35
- export function templateDir(home) {
36
- return join(home, "template", "pr");
37
- }
38
-
39
- export async function listTemplates(home) {
40
- let entries;
41
- try {
42
- entries = await fs.readdir(templateDir(home));
43
- } catch (err) {
44
- if (err.code === "ENOENT") return [];
45
- throw err;
46
- }
47
- return entries
48
- .filter((f) => f.endsWith(".md"))
49
- .map((f) => f.slice(0, -3))
50
- .sort();
51
- }
52
-
53
- async function readBundledDefault() {
54
- return fs.readFile(DEFAULT_TEMPLATE_PATH, "utf8");
55
- }
56
-
57
- export async function readTemplate(home, name) {
58
- if (!name || name === "default") return readBundledDefault();
59
- if (!isValidTemplateName(name)) throw new Error(`invalid template name: ${name}`);
60
- try {
61
- return await fs.readFile(join(templateDir(home), `${name}.md`), "utf8");
62
- } catch (err) {
63
- if (err.code === "ENOENT") return readBundledDefault();
64
- throw err;
65
- }
66
- }
67
-
68
- export async function addTemplate(home, name, content, opts = {}) {
69
- if (!isValidTemplateName(name)) throw new Error(`invalid template name: ${name}`);
70
- const dir = templateDir(home);
71
- await fs.mkdir(dir, { recursive: true });
72
- const target = join(dir, `${name}.md`);
73
- if (!opts.force) {
74
- try {
75
- await fs.access(target);
76
- throw new Error(`template exists: ${name} (use --yes to overwrite)`);
77
- } catch (err) {
78
- if (err.code !== "ENOENT") throw err;
79
- }
80
- }
81
- await fs.writeFile(target, content, "utf8");
82
- return target;
83
- }
84
-
85
- function takeValue(args, i, flag) {
86
- const v = args[i + 1];
87
- if (!v || v.startsWith("--")) throw new Error(`${flag} requires a value`);
88
- return v;
89
- }
90
-
91
- async function readAddContent(opts) {
92
- if (opts.content !== null) return opts.content;
93
- if (opts.file !== null) return fs.readFile(resolve(opts.file), "utf8");
94
- throw new Error("template add requires --content or --file");
95
- }
96
-
97
- async function confirm(question) {
98
- const rl = createInterface({ input, output });
99
- try {
100
- const answer = (await rl.question(`${question} [y/N] `)).trim().toLowerCase();
101
- return answer === "y" || answer === "yes";
102
- } finally {
103
- rl.close();
104
- }
105
- }
106
-
107
- async function cmdTemplate(args, home) {
108
- const sub = args[0];
109
- const rest = args.slice(1);
110
- const json = rest.includes("--json");
111
- if (sub === "list") {
112
- const names = await listTemplates(home);
113
- if (json) output.write(JSON.stringify({ templates: names }) + "\n");
114
- else output.write(names.length ? names.join("\n") + "\n" : "(no templates)\n");
115
- return 0;
116
- }
117
- if (sub === "show") {
118
- const name = rest.find((a) => !a.startsWith("--"));
119
- if (!name) throw new Error("template show requires a name");
120
- const body = await readTemplate(home, name);
121
- output.write(json ? JSON.stringify({ name, body }) + "\n" : body);
122
- return 0;
123
- }
124
- if (sub === "path") {
125
- output.write(templateDir(home) + "\n");
126
- return 0;
127
- }
128
- if (sub === "add") {
129
- const opts = { name: null, content: null, file: null, yes: false };
130
- for (let i = 0; i < rest.length; i++) {
131
- const flag = rest[i];
132
- if (flag === "--name") opts.name = takeValue(rest, i++, flag);
133
- else if (flag === "--content") opts.content = takeValue(rest, i++, flag);
134
- else if (flag === "--file") opts.file = takeValue(rest, i++, flag);
135
- else if (flag === "--yes") opts.yes = true;
136
- else throw new Error(`unknown flag ${flag}`);
137
- }
138
- if (!opts.name) throw new Error("template add requires --name");
139
- const content = await readAddContent(opts);
140
- const dir = templateDir(home);
141
- const target = join(dir, `${opts.name}.md`);
142
- let force = opts.yes;
143
- if (!force && isValidTemplateName(opts.name)) {
144
- let exists = false;
145
- try {
146
- await fs.access(target);
147
- exists = true;
148
- } catch (err) {
149
- if (err.code !== "ENOENT") throw err;
150
- }
151
- if (exists) {
152
- if (!process.stdin.isTTY) {
153
- throw new Error(`template exists: ${opts.name} (use --yes to overwrite)`);
154
- }
155
- force = await confirm(`Overwrite existing template ${opts.name}?`);
156
- if (!force) { output.write("aborted\n"); return 1; }
157
- }
158
- }
159
- const saved = await addTemplate(home, opts.name, content, { force: true });
160
- output.write(`saved: ${saved}\n`);
161
- return 0;
162
- }
163
- throw new Error(`unknown template subcommand: ${sub ?? "(none)"}`);
164
- }
165
-
166
- function tryGit(cwd, args) {
167
- try {
168
- return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
169
- } catch {
170
- return null;
171
- }
172
- }
173
-
174
- export function recommendBranches(cwd) {
175
- const currentBranch = tryGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
176
- if (currentBranch === null) return { currentBranch: null, recommended: [] };
177
- const ordered = [];
178
- const remoteDefault = tryGit(cwd, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]);
179
- if (remoteDefault) {
180
- const bare = remoteDefault.replace(/^origin\//, "");
181
- if (tryGit(cwd, ["rev-parse", "--verify", "--quiet", `${bare}^{commit}`]) !== null) ordered.push(bare);
182
- else if (tryGit(cwd, ["rev-parse", "--verify", "--quiet", `${remoteDefault}^{commit}`]) !== null) ordered.push(remoteDefault);
183
- }
184
- for (const b of ["main", "master", "develop"]) {
185
- const local = tryGit(cwd, ["show-ref", "--verify", "--quiet", `refs/heads/${b}`]);
186
- if (local !== null) ordered.push(b);
187
- }
188
- const recent = tryGit(cwd, [
189
- "for-each-ref", "--sort=-committerdate", "--format=%(refname:short)", "refs/heads",
190
- ]);
191
- if (recent) ordered.push(...recent.split("\n").filter(Boolean));
192
- const recommended = [];
193
- for (const b of ordered) {
194
- if (b && b !== currentBranch && !recommended.includes(b)) recommended.push(b);
195
- }
196
- return { currentBranch, recommended };
197
- }
198
-
199
- function cmdBranches(args, cwd) {
200
- const json = args.includes("--json");
201
- const r = recommendBranches(cwd);
202
- if (json) output.write(JSON.stringify(r) + "\n");
203
- else output.write((r.recommended.join("\n") || "(no branches)") + "\n");
204
- return 0;
205
- }
206
-
207
- export async function buildGenBundle({ cwd, home, base, templateName }) {
208
- if (!base) throw new Error("gen requires --base");
209
- const verified = tryGit(cwd, ["rev-parse", "--verify", "--quiet", `${base}^{commit}`]);
210
- if (verified === null) throw new Error(`base ref not found: ${base}`);
211
- const currentBranch = tryGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
212
- const logRaw = tryGit(cwd, [
213
- "log", "--reverse", "--format=%h%x09%s", `${base}..HEAD`,
214
- ]) || "";
215
- const commits = logRaw
216
- .split("\n")
217
- .filter(Boolean)
218
- .map((line) => {
219
- const tab = line.indexOf("\t");
220
- return { hash: line.slice(0, tab), subject: line.slice(tab + 1) };
221
- });
222
- const diffStat = tryGit(cwd, ["diff", "--stat", `${base}...HEAD`]) || "";
223
- const template = await readTemplate(home, templateName);
224
- const resolvedName = templateName && templateName !== "default"
225
- ? templateName
226
- : "default";
227
- return { base, currentBranch, templateName: resolvedName, template, commits, diffStat };
228
- }
229
-
230
- async function cmdGen(args, cwd, home) {
231
- const opts = { base: null, template: "default" };
232
- for (let i = 0; i < args.length; i++) {
233
- const flag = args[i];
234
- if (flag === "--base") opts.base = takeValue(args, i++, flag);
235
- else if (flag === "--template") opts.template = takeValue(args, i++, flag);
236
- else if (flag === "--json") continue;
237
- else throw new Error(`unknown flag ${flag}`);
238
- }
239
- const bundle = await buildGenBundle({ cwd, home, base: opts.base, templateName: opts.template });
240
- output.write(JSON.stringify(bundle) + "\n");
241
- return 0;
242
- }
243
-
244
- export async function run(args) {
245
- if (args.length === 0 || args.includes("--help")) {
246
- output.write(USAGE);
247
- return 0;
248
- }
249
- const home = resolveOkstraHome();
250
- const sub = args[0];
251
- const rest = args.slice(1);
252
- try {
253
- if (sub === "template") return await cmdTemplate(rest, home);
254
- if (sub === "branches") return cmdBranches(rest, process.cwd());
255
- if (sub === "gen") return await cmdGen(rest, process.cwd(), home);
256
- throw new Error(`unknown pr subcommand: ${sub}`);
257
- } catch (err) {
258
- process.stderr.write(`error: ${err.message}\n`);
259
- return 1;
260
- }
261
- }
@@ -1,176 +0,0 @@
1
- import { homedir } from "node:os";
2
-
3
- import { runPythonModule } from "./python-helper.mjs";
4
-
5
- export const RUNTIME_AUTO = "auto";
6
-
7
- const HOST_REQUEST_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
8
- const PLAIN_TEXT_INPUT = "plain_text_input";
9
-
10
- export class HostRegistryClientError extends Error {
11
- constructor(message) {
12
- super(message);
13
- this.name = "HostRegistryClientError";
14
- }
15
- }
16
-
17
- async function callHosts(runPython, args) {
18
- const result = await runPython({
19
- module: "okstra_ctl.entrypoints.hosts",
20
- args,
21
- stdio: "capture",
22
- });
23
- if (result.code !== 0) {
24
- const detail = String(result.stderr || "").trim() || `Python exited ${result.code}`;
25
- throw new HostRegistryClientError(detail);
26
- }
27
- const stdout = String(result.stdout || "").trim();
28
- if (!stdout) throw new HostRegistryClientError("host registry returned empty stdout");
29
- try {
30
- return JSON.parse(stdout);
31
- } catch (error) {
32
- throw new HostRegistryClientError(`host registry returned invalid JSON: ${error.message}`);
33
- }
34
- }
35
-
36
- function resolutionContext(env = {}, capabilities = {}) {
37
- return {
38
- env: { OKSTRA_RUNTIME_HOST: String(env?.OKSTRA_RUNTIME_HOST ?? "") },
39
- capabilities: {
40
- claudeSkillHandoff: capabilities?.claudeSkillHandoff === true,
41
- tmuxAvailable: capabilities?.tmuxAvailable === true,
42
- },
43
- };
44
- }
45
-
46
- export function createHostRegistryClient({ runPython = runPythonModule } = {}) {
47
- return {
48
- async catalog() {
49
- return await callHosts(runPython, ["catalog", "--json"]);
50
- },
51
- async resolveHostRuntime({ requestedRuntime, command, env, capabilities }) {
52
- return await callHosts(runPython, [
53
- "resolve",
54
- "--requested", requestedRuntime,
55
- "--command", command,
56
- "--context-json", JSON.stringify(resolutionContext(env, capabilities)),
57
- ]);
58
- },
59
- async probeHost({
60
- hostRuntime,
61
- entryMode,
62
- projectRoot,
63
- homeDir,
64
- availableFunctions = [],
65
- }) {
66
- const args = [
67
- "probe",
68
- "--host", hostRuntime,
69
- "--entry-mode", entryMode,
70
- "--project-root", projectRoot,
71
- "--home-dir", homeDir,
72
- ];
73
- for (const availableFunction of availableFunctions) {
74
- args.push("--available-function", availableFunction);
75
- }
76
- return await callHosts(runPython, args);
77
- },
78
- };
79
- }
80
-
81
- const hostRegistryClient = createHostRegistryClient();
82
-
83
- export function normalizeHostRequest(value) {
84
- const runtime = String(value ?? "").trim() || RUNTIME_AUTO;
85
- if (!HOST_REQUEST_PATTERN.test(runtime)) {
86
- throw new Error(
87
- `invalid runtime '${runtime}' (expected auto, all, or a lowercase host ID)`,
88
- );
89
- }
90
- return runtime;
91
- }
92
-
93
- function failedResolution(requestedRuntime, error) {
94
- return {
95
- ok: false,
96
- requestedRuntime,
97
- resolvedRuntime: null,
98
- launchMode: null,
99
- installTargets: [],
100
- host: "unknown",
101
- confidence: "low",
102
- reason: error.message,
103
- fallbackFrom: null,
104
- warnings: [],
105
- };
106
- }
107
-
108
- function catalogResolution(catalog) {
109
- const hosts = Array.isArray(catalog?.hosts) ? catalog.hosts : [];
110
- const catalogHostIds = hosts.map((host) => host.id);
111
- const installTargets = Array.from(new Set(
112
- hosts.flatMap((host) => host.install_targets ?? host.installTargets ?? []),
113
- )).sort();
114
- return {
115
- ok: true,
116
- requestedRuntime: "all",
117
- resolvedRuntime: "all",
118
- launchMode: "catalog",
119
- installTargets,
120
- catalogHostIds,
121
- host: "explicit",
122
- confidence: "high",
123
- reason: "All registered host runtimes requested.",
124
- fallbackFrom: null,
125
- warnings: [],
126
- };
127
- }
128
-
129
- export async function loadHostCatalog() {
130
- return await hostRegistryClient.catalog();
131
- }
132
-
133
- export async function resolveHostRuntime(options) {
134
- return await hostRegistryClient.resolveHostRuntime(options);
135
- }
136
-
137
- export async function probeHost(options) {
138
- return await hostRegistryClient.probeHost(options);
139
- }
140
-
141
- export async function resolveRuntime({
142
- requestedRuntime = RUNTIME_AUTO,
143
- command = "run",
144
- env = {},
145
- capabilities = {},
146
- } = {}) {
147
- const request = normalizeHostRequest(requestedRuntime);
148
- try {
149
- if (request === "all") return catalogResolution(await loadHostCatalog());
150
- return await resolveHostRuntime({
151
- requestedRuntime: request,
152
- command,
153
- env,
154
- capabilities,
155
- });
156
- } catch (error) {
157
- if (!(error instanceof HostRegistryClientError)) throw error;
158
- return failedResolution(request, error);
159
- }
160
- }
161
-
162
- export async function checkRuntimeReadiness({
163
- runtime,
164
- projectRoot,
165
- homeDir = homedir(),
166
- hostRegistryClient = { probeHost },
167
- }) {
168
- if (!runtime) return { runtime: "auto", status: "ready", checks: [] };
169
- return await hostRegistryClient.probeHost({
170
- hostRuntime: runtime,
171
- entryMode: "current-session",
172
- projectRoot,
173
- homeDir,
174
- availableFunctions: [PLAIN_TEXT_INPUT],
175
- });
176
- }
package/src/lib/paths.mjs DELETED
@@ -1,86 +0,0 @@
1
- import { homedir, tmpdir } from "node:os";
2
- import { join } from "node:path";
3
- import { existsSync } from "node:fs";
4
- import { getPackageRoot, getPackageVersion } from "./version.mjs";
5
-
6
- function isStaleTmpHome(p) {
7
- // tests/conftest.py creates the isolated home as `<tmp_path>/.okstra-isolated`
8
- // under the system temp dir. Only a deleted one (env leaked, dir gone) heals;
9
- // a soon-to-be-created `.okstra` temp home or a custom home is left untouched.
10
- if (!p.endsWith("/.okstra-isolated")) return false;
11
- const roots = [tmpdir(), "/tmp", "/private/tmp", "/var/folders", "/private/var/folders"];
12
- return roots.some(
13
- (r) => r && (p === r || p.startsWith(r.replace(/\/+$/, "") + "/")),
14
- );
15
- }
16
-
17
- // Resolve OKSTRA_HOME, healing a leaked value: when it points at a missing
18
- // system-temp path (e.g. a deleted pytest-isolated home), warn and fall back
19
- // to ~/.okstra instead of operating on the dead directory.
20
- export function resolveOkstraHome() {
21
- const raw = process.env.OKSTRA_HOME?.trim();
22
- if (!raw) return join(homedir(), ".okstra");
23
- if (!existsSync(raw) && isStaleTmpHome(raw)) {
24
- process.stderr.write(
25
- `⚠ OKSTRA_HOME=${raw} points to a missing temp dir (leaked test value); falling back to ~/.okstra\n`,
26
- );
27
- return join(homedir(), ".okstra");
28
- }
29
- return raw;
30
- }
31
-
32
- export async function resolvePaths() {
33
- const home = resolveOkstraHome();
34
- const pkgRoot = getPackageRoot();
35
- const devLink = await readSimpleFile(join(home, "dev-link"));
36
- const runtimeRoot = devLink || join(pkgRoot, "runtime");
37
- return {
38
- agents: join(runtimeRoot, "agents"),
39
- workspace: runtimeRoot,
40
- pythonpath: join(home, "lib", "python"),
41
- bin: join(home, "bin"),
42
- home,
43
- package: await getPackageVersion(),
44
- version: await readSimpleFile(join(home, "version")),
45
- "dev-link": devLink || "",
46
- };
47
- }
48
-
49
- function workspacePythonpath(paths) {
50
- const candidates = [
51
- join(paths.workspace, "python"),
52
- join(paths.workspace, "scripts"),
53
- ];
54
- return candidates.find((candidate) => existsSync(candidate)) || "";
55
- }
56
-
57
- // Compose the PYTHONPATH spawned python3 subprocesses should see.
58
- //
59
- // Precedence (first wins):
60
- // 1. process.env.PYTHONPATH (user override — e.g. in-repo `PYTHONPATH=scripts`
61
- // for dev work without re-running `okstra install`).
62
- // 2. The package/dev-link workspace runtime selected by `okstra paths`.
63
- // 3. The installed user-runtime path (~/.okstra/lib/python).
64
- //
65
- // This is a colon-joined string suitable for direct assignment into the child
66
- // process env. Empty segments are dropped so a missing env var does not produce
67
- // a leading ':' (which Python treats as 'current directory').
68
- export function buildPythonpath(paths) {
69
- const fragments = [
70
- process.env.PYTHONPATH,
71
- workspacePythonpath(paths),
72
- paths.pythonpath,
73
- ].filter(
74
- (s) => typeof s === "string" && s.length > 0,
75
- );
76
- return fragments.join(":");
77
- }
78
-
79
- async function readSimpleFile(path) {
80
- try {
81
- const { readFile } = await import("node:fs/promises");
82
- return (await readFile(path, "utf8")).trim();
83
- } catch {
84
- return "";
85
- }
86
- }
package/src/lib/proc.mjs DELETED
@@ -1,31 +0,0 @@
1
- // Shared filesystem / subprocess helpers used by the Node command modules.
2
- // Extracted to one place so a fix lands once instead of in each command file.
3
- import { spawn } from "node:child_process";
4
- import { promises as fs } from "node:fs";
5
-
6
- export async function fileExists(p) {
7
- try {
8
- await fs.access(p);
9
- return true;
10
- } catch {
11
- return false;
12
- }
13
- }
14
-
15
- // Run `cmd args`, capturing stdout/stderr. `env` holds *additional* vars that
16
- // are merged over the inherited process.env. Never rejects — failures resolve
17
- // with { code: -1, stderr: <message> }.
18
- export function runProcess(cmd, args, env = {}) {
19
- return new Promise((resolve) => {
20
- const child = spawn(cmd, args, {
21
- stdio: ["ignore", "pipe", "pipe"],
22
- env: { ...process.env, ...env },
23
- });
24
- let stdout = "";
25
- let stderr = "";
26
- child.stdout.on("data", (b) => (stdout += b.toString()));
27
- child.stderr.on("data", (b) => (stderr += b.toString()));
28
- child.on("error", (err) => resolve({ code: -1, stdout, stderr: err.message }));
29
- child.on("close", (code) => resolve({ code, stdout, stderr }));
30
- });
31
- }
@@ -1,142 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { existsSync } from "node:fs";
3
- import { join, resolve as resolvePath } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { buildPythonpath, resolvePaths } from "./paths.mjs";
6
-
7
- export function resolveInstalledScript(paths, scriptName) {
8
- // Prefer the installed copy under ~/.okstra/bin (what production users run);
9
- // fall back to the in-repo source when invoked from a checkout that has not
10
- // been installed (dev / CI).
11
- const installed = join(paths.bin, scriptName);
12
- if (existsSync(installed)) return installed;
13
- const repoRoot = fileURLToPath(new URL("../..", import.meta.url));
14
- const dev = resolvePath(repoRoot, "scripts", scriptName);
15
- return existsSync(dev) ? dev : null;
16
- }
17
-
18
- export function resolveInstalledValidator(paths, validatorName) {
19
- // `okstra install` lands the validators tree at ~/.okstra/lib/validators;
20
- // a checkout that has not been installed still has the in-repo source.
21
- const installed = join(paths.home, "lib", "validators", validatorName);
22
- if (existsSync(installed)) return installed;
23
- const repoRoot = fileURLToPath(new URL("../..", import.meta.url));
24
- const dev = resolvePath(repoRoot, "validators", validatorName);
25
- return existsSync(dev) ? dev : null;
26
- }
27
-
28
- async function spawnPythonEntry(entry, args, paths) {
29
- return await new Promise((resolve) => {
30
- const child = spawn("python3", [entry, ...args], {
31
- stdio: "inherit",
32
- env: { ...process.env, PYTHONPATH: buildPythonpath(paths) },
33
- });
34
- child.on("error", (err) => {
35
- process.stderr.write(`error: failed to spawn python3: ${err.message}\n`);
36
- resolve(1);
37
- });
38
- child.on("close", (code) => resolve(typeof code === "number" ? code : 1));
39
- });
40
- }
41
-
42
- // Thin spawn shim shared by every `okstra <cmd>` subcommand that fronts a
43
- // `scripts/okstra-*.py` entry point. Centralizing it keeps PYTHONPATH wiring
44
- // and installed/dev resolution in one place so skills call `okstra <cmd>`
45
- // instead of emitting `python3 "$HOME/..."` (which breaks `Bash(okstra:*)`
46
- // permission matching and prompts on every call).
47
- export async function runInstalledScript({ scriptName, args, usage, emptyArgsCode = 2 }) {
48
- if (args.length === 0) {
49
- process.stdout.write(usage);
50
- return emptyArgsCode;
51
- }
52
- // Only a bare `--help` / `-h` prints the wrapper usage. A `--help` that
53
- // follows a subcommand (e.g. `error-log append-observed --help`) must reach
54
- // the python helper so its own per-subcommand help shows through.
55
- if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
56
- process.stdout.write(usage);
57
- return 0;
58
- }
59
- const paths = await resolvePaths();
60
- const entry = resolveInstalledScript(paths, scriptName);
61
- if (!entry) {
62
- process.stderr.write(
63
- `error: ${scriptName} not found — run 'okstra install' (or 'okstra ensure-installed') first\n`,
64
- );
65
- return 1;
66
- }
67
- return await spawnPythonEntry(entry, args, paths);
68
- }
69
-
70
- export async function runInstalledValidator({ validatorName, args }) {
71
- const paths = await resolvePaths();
72
- const entry = resolveInstalledValidator(paths, validatorName);
73
- if (!entry) {
74
- process.stderr.write(
75
- `error: ${validatorName} not found — run 'okstra install' (or 'okstra ensure-installed') first\n`,
76
- );
77
- return 1;
78
- }
79
- return await spawnPythonEntry(entry, args, paths);
80
- }
81
-
82
- export async function runPythonSnippet({ script, args = [], extraEnv = {} }) {
83
- const paths = await resolvePaths();
84
- return new Promise((resolve) => {
85
- const child = spawn("python3", ["-c", script, ...args], {
86
- stdio: ["ignore", "pipe", "pipe"],
87
- env: { ...process.env, PYTHONPATH: buildPythonpath(paths), ...extraEnv },
88
- });
89
- // Collect chunks and join once: repeated `s += chunk` is O(n^2) for the
90
- // large rendered prompts some capture-mode callers stream through here.
91
- const outChunks = [];
92
- const errChunks = [];
93
- child.stdout.on("data", (b) => outChunks.push(b));
94
- child.stderr.on("data", (b) => errChunks.push(b));
95
- child.on("error", (err) =>
96
- resolve({ code: -1, stdout: Buffer.concat(outChunks).toString(), stderr: err.message }),
97
- );
98
- child.on("close", (code) =>
99
- resolve({
100
- code,
101
- stdout: Buffer.concat(outChunks).toString(),
102
- stderr: Buffer.concat(errChunks).toString(),
103
- }),
104
- );
105
- });
106
- }
107
-
108
- export async function runPythonModule({ module, args = [], extraEnv = {}, stdio = "inherit-stdout" }) {
109
- const paths = await resolvePaths();
110
- return new Promise((resolve) => {
111
- const child = spawn("python3", ["-m", module, ...args], {
112
- stdio: stdio === "capture" ? ["ignore", "pipe", "pipe"] : ["ignore", "inherit", "inherit"],
113
- env: { ...process.env, PYTHONPATH: buildPythonpath(paths), ...extraEnv },
114
- });
115
- // Collect chunks and join once: repeated `s += chunk` is O(n^2) for the
116
- // large rendered prompts some capture-mode callers stream through here.
117
- const outChunks = [];
118
- const errChunks = [];
119
- if (stdio === "capture") {
120
- child.stdout.on("data", (b) => outChunks.push(b));
121
- child.stderr.on("data", (b) => errChunks.push(b));
122
- }
123
- child.on("error", (err) =>
124
- resolve({ code: -1, stdout: Buffer.concat(outChunks).toString(), stderr: err.message }),
125
- );
126
- // A signal-killed child reports code === null; normalize to a non-zero exit
127
- // here so every consumer inherits correct failure semantics from one place.
128
- child.on("close", (code) =>
129
- resolve({
130
- code: typeof code === "number" ? code : 1,
131
- stdout: Buffer.concat(outChunks).toString(),
132
- stderr: Buffer.concat(errChunks).toString(),
133
- }),
134
- );
135
- });
136
- }
137
-
138
- export function emitJsonError({ stage, reason, extra = {} }) {
139
- process.stdout.write(
140
- JSON.stringify({ ok: false, stage, reason, ...extra }, null, 2) + "\n",
141
- );
142
- }