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,104 +0,0 @@
1
- import { resolvePaths } from "../../lib/paths.mjs";
2
-
3
- const USAGE = `okstra paths — print runtime paths
4
-
5
- Usage:
6
- okstra paths Print all paths as JSON
7
- okstra paths --json Same as the default
8
- okstra paths --field <key> Print one field as a plain string
9
- okstra paths --shell Print shell 'export' statements for the
10
- standard OKSTRA_* env vars (sourced by skills)
11
-
12
- Fields:
13
- agents Absolute path to agents/. Default: <workspace>/agents.
14
- workspace Directory containing prompts/, templates/, validators/,
15
- agents/. Equals the package's runtime/ directory in copy
16
- mode, or the dev-link repo path in dev mode.
17
- pythonpath $HOME/.okstra/lib/python
18
- bin $HOME/.okstra/bin
19
- home $HOME/.okstra
20
- version Installed okstra version (from ~/.okstra/version stamp)
21
- package Currently invoked package version
22
- dev-link Repo path recorded by 'okstra install --link <repo>'
23
- (empty string when not in dev mode).
24
-
25
- Shell mode exports (variable name -> field):
26
- OKSTRA_WORKSPACE workspace
27
- OKSTRA_AGENTS_DIR agents
28
- OKSTRA_PYTHONPATH pythonpath
29
- OKSTRA_BIN bin
30
- OKSTRA_HOME home
31
- `;
32
-
33
- const VALID_FIELDS = new Set([
34
- "agents",
35
- "workspace",
36
- "pythonpath",
37
- "bin",
38
- "home",
39
- "version",
40
- "package",
41
- "dev-link",
42
- ]);
43
-
44
- const SHELL_EXPORTS = [
45
- ["OKSTRA_WORKSPACE", "workspace"],
46
- ["OKSTRA_AGENTS_DIR", "agents"],
47
- ["OKSTRA_PYTHONPATH", "pythonpath"],
48
- ["OKSTRA_BIN", "bin"],
49
- ["OKSTRA_HOME", "home"],
50
- ];
51
-
52
- export async function run(args) {
53
- if (args.includes("--help") || args.includes("-h")) {
54
- process.stdout.write(USAGE);
55
- return 0;
56
- }
57
-
58
- let field = null;
59
- let mode = "json";
60
- for (let i = 0; i < args.length; i++) {
61
- const a = args[i];
62
- if (a === "--json") {
63
- mode = "json";
64
- } else if (a === "--shell") {
65
- mode = "shell";
66
- } else if (a === "--field") {
67
- field = args[i + 1];
68
- i++;
69
- if (!field) {
70
- process.stderr.write("error: --field requires a name\n");
71
- return 2;
72
- }
73
- if (!VALID_FIELDS.has(field)) {
74
- process.stderr.write(`error: unknown field '${field}'. Valid: ${[...VALID_FIELDS].join(", ")}\n`);
75
- return 2;
76
- }
77
- mode = "field";
78
- } else {
79
- process.stderr.write(`error: unknown argument '${a}'\n\n${USAGE}`);
80
- return 2;
81
- }
82
- }
83
-
84
- const paths = await resolvePaths();
85
-
86
- if (mode === "field") {
87
- process.stdout.write(`${paths[field]}\n`);
88
- return 0;
89
- }
90
- if (mode === "shell") {
91
- for (const [varName, key] of SHELL_EXPORTS) {
92
- process.stdout.write(`export ${varName}=${shellQuote(paths[key])}\n`);
93
- }
94
- return 0;
95
- }
96
- process.stdout.write(`${JSON.stringify(paths, null, 2)}\n`);
97
- return 0;
98
- }
99
-
100
- function shellQuote(value) {
101
- if (value === "" || value == null) return "''";
102
- if (/^[A-Za-z0-9_\/.\-+:@%=]+$/.test(value)) return value;
103
- return `'${String(value).replace(/'/g, "'\\''")}'`;
104
- }
@@ -1,125 +0,0 @@
1
- import { checkProject } from "./check-project.mjs";
2
- import { runEnsureInstalled } from "./install.mjs";
3
- import { missingHelperScripts } from "../../lib/helper-scripts.mjs";
4
- import { resolveInstalledScript } from "../../lib/python-helper.mjs";
5
- import { resolvePaths } from "../../lib/paths.mjs";
6
- import { checkRuntimeReadiness } from "../../lib/host-registry-client.mjs";
7
-
8
- const USAGE = `okstra preflight — one-call skill preflight (ensure-installed + check-project)
9
-
10
- Usage:
11
- okstra preflight [--runtime <name>] [--cwd <dir>] [--json]
12
-
13
- Runs the runtime freshness check (quietly reinstalling when stale), then
14
- verifies the target project has .okstra/project.json. Prints one JSON object:
15
-
16
- ok: true {ok, projectRoot, projectJsonPath, projectId, runtimeReadiness}
17
- ok: false {ok, stage, reason, ...}
18
- stage: install | helper_scripts_missing | resolve |
19
- project_json_missing | project_json_invalid |
20
- runtime_readiness | python | parse
21
-
22
- Options:
23
- --runtime <name> Runtime whose assets must be present (e.g. claude-code);
24
- forwarded to ensure-installed.
25
- --cwd <dir> Resolve PROJECT_ROOT from <dir> instead of process cwd.
26
- --json Default output format (kept for symmetry).
27
-
28
- Exit codes:
29
- 0 runtime fresh, project registered, and current host ready
30
- 1 runtime install failed, project setup missing/invalid, or host not ready
31
- 2 PROJECT_ROOT could not be resolved from cwd (pass --cwd), or bad flags
32
- `;
33
-
34
- function parseArgs(args) {
35
- const opts = { cwd: process.cwd(), runtime: null };
36
- for (let i = 0; i < args.length; i++) {
37
- const a = args[i];
38
- if (a === "--json") continue;
39
- if (a === "--cwd" || a === "--runtime") {
40
- const next = args[i + 1];
41
- if (!next || next.startsWith("--")) throw new Error(`${a} requires a value`);
42
- if (a === "--cwd") opts.cwd = next;
43
- else opts.runtime = next;
44
- i++;
45
- } else {
46
- throw new Error(`unknown argument '${a}'`);
47
- }
48
- }
49
- return opts;
50
- }
51
-
52
- function emit(payload) {
53
- process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
54
- }
55
-
56
- // A BLOCKING lead step calls these mid-run; resolving them here turns a stale
57
- // install into an up-front failure instead of a dead end hours into a run.
58
- async function findMissingHelperScripts() {
59
- return missingHelperScripts(await resolvePaths(), resolveInstalledScript);
60
- }
61
-
62
- export async function run(args) {
63
- if (args.includes("--help") || args.includes("-h")) {
64
- process.stdout.write(USAGE);
65
- return 0;
66
- }
67
-
68
- let opts;
69
- try {
70
- opts = parseArgs(args);
71
- } catch (err) {
72
- process.stderr.write(`error: ${err.message}\n\n${USAGE}`);
73
- return 2;
74
- }
75
-
76
- const ensureArgs = ["--quiet"];
77
- if (opts.runtime) ensureArgs.push("--runtime", opts.runtime);
78
- const installCode = await runEnsureInstalled(ensureArgs);
79
- if (installCode !== 0) {
80
- emit({
81
- ok: false,
82
- stage: "install",
83
- reason:
84
- "okstra runtime install failed — run 'okstra doctor' to diagnose, " +
85
- "then 'okstra install' to repair.",
86
- });
87
- return installCode === 2 ? 2 : 1;
88
- }
89
-
90
- const missingHelpers = await findMissingHelperScripts();
91
- if (missingHelpers.length > 0) {
92
- emit({
93
- ok: false,
94
- stage: "helper_scripts_missing",
95
- missingHelpers,
96
- reason:
97
- `${missingHelpers.length} helper script(s) a lead prompt can call are not ` +
98
- "installed — run 'okstra install' to repair before starting a run.",
99
- });
100
- return 1;
101
- }
102
-
103
- const { code, payload } = await checkProject({ cwd: opts.cwd });
104
- if (code !== 0) {
105
- emit(payload);
106
- return code;
107
- }
108
-
109
- const runtimeReadiness = await checkRuntimeReadiness({
110
- runtime: opts.runtime,
111
- projectRoot: payload.projectRoot,
112
- });
113
- if (["blocked", "unknown"].includes(runtimeReadiness.status)) {
114
- emit({
115
- ...payload,
116
- ok: false,
117
- stage: "runtime_readiness",
118
- reason: `current host readiness is ${runtimeReadiness.status}`,
119
- runtimeReadiness,
120
- });
121
- return 1;
122
- }
123
- emit({ ...payload, runtimeReadiness });
124
- return code;
125
- }
@@ -1,333 +0,0 @@
1
- import { promises as fs } from "node:fs";
2
- import { createInterface } from "node:readline";
3
- import { homedir } from "node:os";
4
- import { join, resolve as resolvePath } from "node:path";
5
- import { buildPythonpath, resolvePaths } from "../../lib/paths.mjs";
6
- import { fileExists, runProcess } from "../../lib/proc.mjs";
7
-
8
- const USAGE = `okstra setup — register the current project with okstra
9
-
10
- Writes <PROJECT_ROOT>/.okstra/project.json. This is the
11
- project-level companion to 'okstra install' (which is machine-level).
12
- Inside a Claude Code session this is also exposed as the /okstra-setup
13
- slash command.
14
-
15
- Usage:
16
- okstra setup --project-id <id> Register with explicit projectId
17
- okstra setup Use existing project.json or
18
- prompt for projectId (TTY only)
19
- okstra setup --project-root <path> Override PROJECT_ROOT resolution
20
- okstra setup --yes Skip prompts; require all inputs
21
- on the command line
22
-
23
- Behavior:
24
- - If project.json already exists, the projectId must match (okstra refuses
25
- to silently rename a project). Delete the file manually if you really
26
- want to change projectId.
27
- - If --project-id is omitted and stdin is a TTY, you are prompted.
28
- - If --project-id is omitted and stdin is not a TTY, the command exits
29
- with an error (use --project-id for CI / scripts).
30
-
31
- Exit codes:
32
- 0 project.json present and valid after the run
33
- 1 I/O / python failure or projectId mismatch
34
- 2 PROJECT_ROOT could not be resolved
35
- `;
36
-
37
- function parseArgs(args) {
38
- const opts = {
39
- projectId: null,
40
- projectRoot: null,
41
- yes: false,
42
- };
43
- for (let i = 0; i < args.length; i++) {
44
- const a = args[i];
45
- if (a === "--yes" || a === "-y") opts.yes = true;
46
- else if (a === "--project-id") {
47
- const next = args[i + 1];
48
- if (!next || next.startsWith("--")) throw new Error("--project-id requires a value");
49
- opts.projectId = next;
50
- i++;
51
- } else if (a === "--project-root") {
52
- const next = args[i + 1];
53
- if (!next || next.startsWith("--")) throw new Error("--project-root requires a path");
54
- opts.projectRoot = next;
55
- i++;
56
- } else {
57
- throw new Error(`unknown argument '${a}'`);
58
- }
59
- }
60
- return opts;
61
- }
62
-
63
-
64
- function prompt(question) {
65
- return new Promise((resolve) => {
66
- const rl = createInterface({ input: process.stdin, output: process.stderr });
67
- rl.question(question, (answer) => {
68
- rl.close();
69
- resolve(answer.trim());
70
- });
71
- });
72
- }
73
-
74
- function validateProjectId(id) {
75
- if (!id) return "project-id is empty";
76
- if (!/[A-Za-z0-9]/.test(id)) return "project-id must contain at least one alphanumeric character";
77
- return null;
78
- }
79
-
80
- async function resolveProjectRoot(paths, explicit) {
81
- const probe = await runProcess(
82
- "python3",
83
- [
84
- "-c",
85
- [
86
- "import sys",
87
- "from okstra_project import resolve_project_root, project_json_path, ResolverError",
88
- "try:",
89
- " pr = resolve_project_root(explicit_root=sys.argv[1], cwd=sys.argv[2])",
90
- " print('PROJECT_ROOT', pr)",
91
- " print('PROJECT_JSON', project_json_path(pr))",
92
- "except ResolverError as e:",
93
- " print('RESOLVER_ERROR', e)",
94
- ].join("\n"),
95
- explicit || "",
96
- process.cwd(),
97
- ],
98
- { PYTHONPATH: buildPythonpath(paths) },
99
- );
100
- if (probe.code !== 0) {
101
- throw new Error(`python invocation failed: ${probe.stderr.trim() || probe.stdout.trim()}`);
102
- }
103
- const lines = probe.stdout.trim().split("\n");
104
- const tagOf = (key) =>
105
- lines
106
- .find((l) => l.startsWith(key + " "))
107
- ?.slice(key.length + 1)
108
- .trim() ?? null;
109
- const resolverError = tagOf("RESOLVER_ERROR");
110
- if (resolverError) {
111
- const err = new Error(resolverError);
112
- err.code = "RESOLVER";
113
- throw err;
114
- }
115
- return {
116
- projectRoot: tagOf("PROJECT_ROOT"),
117
- projectJsonPath: tagOf("PROJECT_JSON"),
118
- };
119
- }
120
-
121
- async function upsert(paths, projectRoot, projectId) {
122
- const probe = await runProcess(
123
- "python3",
124
- [
125
- "-c",
126
- [
127
- "import json, sys",
128
- "from pathlib import Path",
129
- "from okstra_project import upsert_project_json, ResolverError",
130
- "try:",
131
- " result = upsert_project_json(Path(sys.argv[1]), sys.argv[2])",
132
- " print('OK', json.dumps(result))",
133
- "except ResolverError as e:",
134
- " print('ERROR', e)",
135
- ].join("\n"),
136
- projectRoot,
137
- projectId,
138
- ],
139
- { PYTHONPATH: buildPythonpath(paths) },
140
- );
141
- if (probe.code !== 0) {
142
- throw new Error(`python invocation failed: ${probe.stderr.trim() || probe.stdout.trim()}`);
143
- }
144
- const out = probe.stdout.trim();
145
- if (out.startsWith("OK ")) {
146
- return JSON.parse(out.slice(3));
147
- }
148
- if (out.startsWith("ERROR ")) {
149
- throw new Error(out.slice(6));
150
- }
151
- throw new Error(`unexpected upsert output: ${out}`);
152
- }
153
-
154
- export async function run(args) {
155
- if (args.includes("--help") || args.includes("-h")) {
156
- process.stdout.write(USAGE);
157
- return 0;
158
- }
159
-
160
- let opts;
161
- try {
162
- opts = parseArgs(args);
163
- } catch (err) {
164
- process.stderr.write(`error: ${err.message}\n\n${USAGE}`);
165
- return 1;
166
- }
167
-
168
- const paths = await resolvePaths();
169
-
170
- let resolved;
171
- try {
172
- resolved = await resolveProjectRoot(paths, opts.projectRoot);
173
- } catch (err) {
174
- if (err.code === "RESOLVER") {
175
- const cwd = resolvePath(process.cwd());
176
- const home = resolvePath(homedir());
177
- const inHome = cwd === home;
178
- process.stderr.write(
179
- "error: 'okstra setup' registers the CURRENT directory as an okstra project,\n" +
180
- " but no project root could be resolved from here.\n" +
181
- ` cwd: ${cwd}\n` +
182
- (inHome
183
- ? " (this looks like your home directory — setup is project-level, not machine-level;\n" +
184
- " machine-level install is 'okstra install', which you've likely already done.)\n"
185
- : "") +
186
- "\nFix one of:\n" +
187
- " 1. cd into the project you want to register, then re-run:\n" +
188
- " cd <your project> && okstra setup --project-id <id>\n" +
189
- " 2. pass an explicit path:\n" +
190
- " okstra setup --project-root <abs-path> --project-id <id>\n" +
191
- " 3. run from inside any git working tree (the toplevel becomes PROJECT_ROOT).\n" +
192
- `\n(underlying error: ${err.message})\n`,
193
- );
194
- return 2;
195
- }
196
- // Non-RESOLVER path: typically python invocation failure (PYTHONPATH wrong,
197
- // ~/.okstra missing, python3 not on PATH, okstra_project module missing).
198
- // The user can't fix this by adjusting setup arguments — it's a runtime
199
- // health issue, not a PROJECT_ROOT issue. Reword accordingly.
200
- const looksLikePythonFailure = /python invocation failed|ModuleNotFoundError|No module named/.test(
201
- err.message,
202
- );
203
- if (looksLikePythonFailure) {
204
- process.stderr.write(
205
- "error: 'okstra setup' could not start because the okstra runtime is not\n" +
206
- " reachable from this process. This is almost always a stale or\n" +
207
- " incomplete install, not a problem with your setup arguments.\n" +
208
- "\nFix:\n" +
209
- " 1. run 'okstra doctor' to see exactly what is missing\n" +
210
- " 2. run 'okstra ensure-installed' (or 'npx -y okstra@latest install')\n" +
211
- " to repair the runtime\n" +
212
- " 3. retry: okstra setup --project-id <id>\n" +
213
- `\n(underlying error: ${err.message})\n`,
214
- );
215
- return 1;
216
- }
217
- process.stderr.write(`error: could not resolve PROJECT_ROOT: ${err.message}\n`);
218
- return 1;
219
- }
220
- const { projectRoot, projectJsonPath } = resolved;
221
-
222
- let existing = null;
223
- if (await fileExists(projectJsonPath)) {
224
- try {
225
- existing = JSON.parse(await fs.readFile(projectJsonPath, "utf8"));
226
- } catch (err) {
227
- process.stderr.write(`error: failed to parse ${projectJsonPath}: ${err.message}\n`);
228
- return 1;
229
- }
230
- }
231
-
232
- let projectId = opts.projectId;
233
- if (!projectId && existing?.projectId) {
234
- projectId = existing.projectId;
235
- }
236
-
237
- if (!projectId) {
238
- if (opts.yes || !process.stdin.isTTY) {
239
- process.stderr.write(
240
- `error: --project-id is required (no existing project.json, not a TTY)\n`,
241
- );
242
- return 1;
243
- }
244
- process.stderr.write(`PROJECT_ROOT: ${projectRoot}\n`);
245
- const answer = await prompt("project-id (e.g. INV-1234, my-app): ");
246
- projectId = answer;
247
- }
248
-
249
- const invalid = validateProjectId(projectId);
250
- if (invalid) {
251
- process.stderr.write(`error: ${invalid}\n`);
252
- return 1;
253
- }
254
-
255
- let result;
256
- try {
257
- result = await upsert(paths, projectRoot, projectId);
258
- } catch (err) {
259
- process.stderr.write(`error: ${err.message}\n`);
260
- return 1;
261
- }
262
-
263
- let settingsSymlink = null;
264
- try {
265
- settingsSymlink = await ensureProjectSettingsSymlink(projectRoot);
266
- } catch (err) {
267
- process.stderr.write(
268
- `warning: failed to provision .claude/settings.local.json symlink — ` +
269
- `host Claude Code sessions in this project may need to add wrapper permissions manually. (${err.message})\n`,
270
- );
271
- }
272
-
273
- process.stdout.write(
274
- JSON.stringify(
275
- {
276
- ok: true,
277
- ...result,
278
- projectJsonPath,
279
- settingsLocalJson: settingsSymlink,
280
- },
281
- null,
282
- 2,
283
- ) + "\n",
284
- );
285
- return 0;
286
- }
287
-
288
- async function ensureProjectSettingsSymlink(projectRoot) {
289
- const template = join(homedir(), ".okstra", "templates", "settings.local.json");
290
- try {
291
- await fs.access(template);
292
- } catch {
293
- return null; // install hasn't provisioned the template yet (pre-0.14 install)
294
- }
295
-
296
- const claudeDir = join(projectRoot, ".claude");
297
- const target = join(claudeDir, "settings.local.json");
298
- await fs.mkdir(claudeDir, { recursive: true });
299
-
300
- let existingStat;
301
- try {
302
- existingStat = await fs.lstat(target);
303
- } catch {
304
- existingStat = null;
305
- }
306
-
307
- if (existingStat?.isSymbolicLink()) {
308
- const current = await fs.readlink(target);
309
- const resolved = current.startsWith("/") ? current : join(claudeDir, current);
310
- if (resolved === template) return target;
311
- await backupAndReplace(target, template);
312
- return target;
313
- }
314
- if (existingStat) {
315
- await backupAndReplace(target, template);
316
- return target;
317
- }
318
-
319
- await fs.symlink(template, target);
320
- return target;
321
- }
322
-
323
- async function backupAndReplace(target, template) {
324
- const stamp = new Date()
325
- .toISOString()
326
- .replace(/[-:]/g, "")
327
- .replace(/\..*/, "")
328
- .replace("T", "-");
329
- const backup = `${target}.bak.${stamp}`;
330
- await fs.rename(target, backup);
331
- await fs.symlink(template, target);
332
- }
333
-