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,6 +1,5 @@
1
1
  import { runPythonModule } from "../lib/python-helper.mjs";
2
2
  import { resolvePaths } from "../lib/paths.mjs";
3
-
4
3
  const USAGE = `okstra manager — manage cross-project okstra tasks
5
4
 
6
5
  Usage:
@@ -17,41 +16,35 @@ Usage:
17
16
 
18
17
  --workspace-root is owned by this command.
19
18
  `;
20
-
21
19
  const OWNED_FLAGS = new Set(["--workspace-root"]);
22
-
23
20
  function isOwnedFlag(arg) {
24
- if (OWNED_FLAGS.has(arg)) return true;
25
- return [...OWNED_FLAGS].some((flag) => arg.startsWith(`${flag}=`));
21
+ if (OWNED_FLAGS.has(arg))
22
+ return true;
23
+ return [...OWNED_FLAGS].some((flag) => arg.startsWith(`${flag}=`));
26
24
  }
27
-
28
25
  export function buildManagerArgs(args, paths) {
29
- return ["--workspace-root", paths.workspace, ...args];
26
+ return ["--workspace-root", paths.workspace, ...args];
30
27
  }
31
-
32
28
  export async function run(args) {
33
- const forbidden = args.find(isOwnedFlag);
34
- if (forbidden) {
35
- process.stderr.write(
36
- `error: ${forbidden} is set by 'okstra manager' itself — remove it from your args\n`,
37
- );
38
- return 2;
39
- }
40
-
41
- if (args.includes("--help") || args.includes("-h")) {
42
- process.stdout.write(USAGE);
43
- return 0;
44
- }
45
- if (args.length === 0) {
46
- process.stdout.write(USAGE);
47
- return 2;
48
- }
49
-
50
- const paths = await resolvePaths();
51
- const result = await runPythonModule({
52
- module: "okstra_ctl.manager_cli",
53
- args: buildManagerArgs(args, paths),
54
- stdio: "inherit-stdout",
55
- });
56
- return result.code;
29
+ const forbidden = args.find(isOwnedFlag);
30
+ if (forbidden) {
31
+ process.stderr.write(`error: ${forbidden} is set by 'okstra manager' itself — remove it from your args\n`);
32
+ return 2;
33
+ }
34
+ if (args.includes("--help") || args.includes("-h")) {
35
+ process.stdout.write(USAGE);
36
+ return 0;
37
+ }
38
+ if (args.length === 0) {
39
+ process.stdout.write(USAGE);
40
+ return 2;
41
+ }
42
+ const paths = await resolvePaths();
43
+ const result = await runPythonModule({
44
+ module: "okstra_ctl.manager_cli",
45
+ args: buildManagerArgs(args, paths),
46
+ stdio: "inherit-stdout",
47
+ });
48
+ return result.code;
57
49
  }
50
+ //# sourceMappingURL=manager.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.mjs","sourceRoot":"","sources":["../../src/commands/manager.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;CAeb,CAAC;AAEF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAElD,SAAS,WAAW,CAAC,GAAW;IAC9B,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAa,EAAE,KAAmB;IACjE,OAAO,CAAC,kBAAkB,EAAE,KAAK,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAuB;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACzC,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,UAAU,SAAS,iEAAiE,CACrF,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,YAAY,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC;QACnC,MAAM,EAAE,wBAAwB;QAChC,IAAI,EAAE,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC;QACnC,KAAK,EAAE,gBAAgB;KACxB,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function run(args: readonly string[]): Promise<number>;
@@ -0,0 +1,569 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ import { randomBytes } from "node:crypto";
4
+ import { promises as fs } from "node:fs";
5
+ import { basename, dirname, join, relative, resolve } from "node:path";
6
+ import { resolveOkstraHome } from "../../lib/paths.mjs";
7
+ const MEMORY_TYPES = new Set([
8
+ "context",
9
+ "decision",
10
+ "preference",
11
+ "requirement",
12
+ "person",
13
+ "project-hint",
14
+ "follow-up",
15
+ ]);
16
+ function isMemoryType(value) {
17
+ return MEMORY_TYPES.has(value);
18
+ }
19
+ function isRecord(value) {
20
+ return typeof value === "object" && value !== null;
21
+ }
22
+ // High-confidence secret shapes. The skill instructs the agent to omit secrets,
23
+ // but this is the CLI-side enforcement (declaration → enforcement) so a stray
24
+ // credential cannot be persisted. Patterns are intentionally narrow (known token
25
+ // prefixes / key blocks) to avoid false-positives on prose that merely mentions
26
+ // "password" or "token".
27
+ const SECRET_PATTERNS = [
28
+ [/-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/, "private key block"],
29
+ [/\bAKIA[0-9A-Z]{16}\b/, "AWS access key id"],
30
+ [/\bASIA[0-9A-Z]{16}\b/, "AWS temporary access key id"],
31
+ [/\bgh[posru]_[A-Za-z0-9]{36,}\b/, "GitHub token"],
32
+ [/\bgithub_pat_[A-Za-z0-9_]{60,}\b/, "GitHub fine-grained token"],
33
+ [/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/, "Slack token"],
34
+ [/\bAIza[0-9A-Za-z_-]{35}\b/, "Google API key"],
35
+ [/\bsk-[A-Za-z0-9]{20,}\b/, "OpenAI-style secret key"],
36
+ [/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/, "JWT"],
37
+ ];
38
+ function detectSecrets(content) {
39
+ const hits = [];
40
+ for (const [pattern, label] of SECRET_PATTERNS) {
41
+ if (pattern.test(content))
42
+ hits.push(label);
43
+ }
44
+ return [...new Set(hits)];
45
+ }
46
+ const USAGE = `okstra memory — store and find conversation memory
47
+
48
+ Memory Book stores global, user-home notes under ~/.okstra/memory-book.
49
+ It is separate from project-local .okstra task artifacts.
50
+
51
+ Usage:
52
+ okstra memory add [--content <text> | --file <path>] [options]
53
+ okstra memory list [--limit <n>] [--tag <tag>] [--type <type>]
54
+ [--project-group <name>] [--json]
55
+ okstra memory search <query> [--limit <n>] [--project-group <name>]
56
+ [--include-archived] [--json]
57
+ okstra memory groups [--include-archived] [--json]
58
+ okstra memory show <id> [--json]
59
+ okstra memory archive <id> [--json]
60
+
61
+ Add options:
62
+ --title <title> Entry title. Defaults to the first non-empty line.
63
+ --type <type> context|decision|preference|requirement|person|
64
+ project-hint|follow-up. Default: context.
65
+ --project-group <name> Project group label (e.g. acme, private). Default: global.
66
+ --project <id> Related project id. Repeatable.
67
+ --tag <tag> Tag. Repeatable or comma-separated.
68
+ --source <source> Source label. Default: conversation.
69
+ --yes Skip interactive confirmation.
70
+ --json Emit JSON.
71
+ `;
72
+ function okstraHome() {
73
+ return resolveOkstraHome();
74
+ }
75
+ function memoryRoot() {
76
+ return join(okstraHome(), "memory-book");
77
+ }
78
+ function indexPath() {
79
+ return join(memoryRoot(), "index.jsonl");
80
+ }
81
+ function parseGlobalFlags(args) {
82
+ return {
83
+ json: args.includes("--json"),
84
+ includeArchived: args.includes("--include-archived"),
85
+ };
86
+ }
87
+ function takeValue(args, index, flag) {
88
+ const value = args[index + 1];
89
+ if (!value || value.startsWith("--")) {
90
+ throw new Error(`${flag} requires a value`);
91
+ }
92
+ return value;
93
+ }
94
+ function parseAddArgs(args) {
95
+ const opts = {
96
+ content: null,
97
+ file: null,
98
+ title: null,
99
+ type: "context",
100
+ projectGroup: "global",
101
+ source: "conversation",
102
+ tags: [],
103
+ projects: [],
104
+ yes: false,
105
+ json: false,
106
+ };
107
+ for (let i = 0; i < args.length; i++) {
108
+ const flag = args[i];
109
+ if (flag === "--content")
110
+ opts.content = takeValue(args, i++, flag);
111
+ else if (flag === "--file")
112
+ opts.file = takeValue(args, i++, flag);
113
+ else if (flag === "--title")
114
+ opts.title = takeValue(args, i++, flag);
115
+ else if (flag === "--type")
116
+ opts.type = takeValue(args, i++, flag);
117
+ else if (flag === "--project-group")
118
+ opts.projectGroup = takeValue(args, i++, flag);
119
+ else if (flag === "--source")
120
+ opts.source = takeValue(args, i++, flag);
121
+ else if (flag === "--project")
122
+ opts.projects.push(takeValue(args, i++, flag));
123
+ else if (flag === "--tag")
124
+ opts.tags.push(...splitCsv(takeValue(args, i++, flag)));
125
+ else if (flag === "--yes")
126
+ opts.yes = true;
127
+ else if (flag === "--json")
128
+ opts.json = true;
129
+ else
130
+ throw new Error(`unknown flag ${flag}`);
131
+ }
132
+ if (!isMemoryType(opts.type)) {
133
+ throw new Error(`invalid --type: ${opts.type}`);
134
+ }
135
+ if (opts.content !== null && opts.file !== null) {
136
+ throw new Error("pass only one of --content or --file");
137
+ }
138
+ return { ...opts, type: opts.type };
139
+ }
140
+ function parseListArgs(args) {
141
+ const opts = { ...parseGlobalFlags(args), limit: 20, tag: null, type: null, projectGroup: null };
142
+ for (let i = 0; i < args.length; i++) {
143
+ const flag = args[i];
144
+ if (flag === undefined)
145
+ continue;
146
+ if (flag === "--json" || flag === "--include-archived")
147
+ continue;
148
+ if (flag === "--limit")
149
+ opts.limit = parseLimit(takeValue(args, i++, flag));
150
+ else if (flag === "--tag")
151
+ opts.tag = takeValue(args, i++, flag);
152
+ else if (flag === "--type")
153
+ opts.type = takeValue(args, i++, flag);
154
+ else if (flag === "--project-group")
155
+ opts.projectGroup = takeValue(args, i++, flag);
156
+ else
157
+ throw new Error(`unknown flag ${flag}`);
158
+ }
159
+ return opts;
160
+ }
161
+ function parseQueryArgs(args) {
162
+ const opts = { ...parseGlobalFlags(args), limit: 20, query: [], projectGroup: null };
163
+ for (let i = 0; i < args.length; i++) {
164
+ const flag = args[i];
165
+ if (flag === undefined)
166
+ continue;
167
+ if (flag === "--json" || flag === "--include-archived")
168
+ continue;
169
+ if (flag === "--limit")
170
+ opts.limit = parseLimit(takeValue(args, i++, flag));
171
+ else if (flag === "--project-group")
172
+ opts.projectGroup = takeValue(args, i++, flag);
173
+ else if (flag.startsWith("--"))
174
+ throw new Error(`unknown flag ${flag}`);
175
+ else
176
+ opts.query.push(flag);
177
+ }
178
+ if (opts.query.length === 0)
179
+ throw new Error("search requires a query");
180
+ return { ...opts, query: opts.query.join(" ").trim() };
181
+ }
182
+ function parseGroupsArgs(args) {
183
+ for (const flag of args) {
184
+ if (flag !== "--json" && flag !== "--include-archived") {
185
+ throw new Error(`unknown flag ${flag}`);
186
+ }
187
+ }
188
+ return parseGlobalFlags(args);
189
+ }
190
+ function entryGroup(entry) {
191
+ return entry.projectGroup ?? "global";
192
+ }
193
+ function parseLimit(raw) {
194
+ const value = Number.parseInt(raw, 10);
195
+ if (!Number.isInteger(value) || value < 1) {
196
+ throw new Error("--limit must be a positive integer");
197
+ }
198
+ return value;
199
+ }
200
+ function splitCsv(value) {
201
+ return value
202
+ .split(",")
203
+ .map((part) => part.trim())
204
+ .filter(Boolean);
205
+ }
206
+ async function readInputContent(opts) {
207
+ if (opts.content !== null)
208
+ return opts.content;
209
+ if (opts.file !== null)
210
+ return fs.readFile(resolve(opts.file), "utf8");
211
+ if (!process.stdin.isTTY)
212
+ return readStdin();
213
+ throw new Error("memory add requires --content, --file, or stdin");
214
+ }
215
+ async function readStdin() {
216
+ const chunks = [];
217
+ for await (const chunk of process.stdin)
218
+ chunks.push(Buffer.from(chunk));
219
+ return Buffer.concat(chunks).toString("utf8");
220
+ }
221
+ function inferTitle(content, explicitTitle, filePath) {
222
+ if (explicitTitle?.trim())
223
+ return explicitTitle.trim();
224
+ const firstLine = content
225
+ .split(/\r?\n/)
226
+ .map((line) => line.trim())
227
+ .find(Boolean);
228
+ if (firstLine)
229
+ return truncate(firstLine.replace(/^#+\s*/, ""), 80);
230
+ if (filePath)
231
+ return basename(filePath);
232
+ return "Untitled memory";
233
+ }
234
+ function truncate(value, maxLength) {
235
+ return value.length > maxLength ? `${value.slice(0, maxLength - 3)}...` : value;
236
+ }
237
+ function slugify(value) {
238
+ const slug = value
239
+ .toLowerCase()
240
+ .replace(/[^a-z0-9가-힣]+/g, "-")
241
+ .replace(/^-+|-+$/g, "")
242
+ .slice(0, 48);
243
+ return slug || "memory";
244
+ }
245
+ function buildEntry(opts, content, now) {
246
+ const title = inferTitle(content, opts.title, opts.file);
247
+ const compact = now.toISOString().replace(/\D/g, "").slice(0, 17);
248
+ const slug = slugify(title);
249
+ // Random suffix disambiguates two same-title adds within one millisecond,
250
+ // which would otherwise collide on both id and on-disk path (silent overwrite).
251
+ const nonce = randomBytes(3).toString("hex");
252
+ const id = `mem_${compact}_${slug}_${nonce}`;
253
+ const relativePath = join("entries", String(now.getUTCFullYear()), String(now.getUTCMonth() + 1).padStart(2, "0"), `${now.toISOString().replace(/[:.]/g, "")}-${slug}-${nonce}.md`);
254
+ return {
255
+ id,
256
+ title,
257
+ type: opts.type,
258
+ projectGroup: opts.projectGroup,
259
+ source: opts.source,
260
+ tags: [...new Set(opts.tags)],
261
+ relatedProjects: [...new Set(opts.projects)],
262
+ status: "active",
263
+ createdAt: now.toISOString(),
264
+ archivedAt: null,
265
+ path: relativePath,
266
+ };
267
+ }
268
+ function renderMarkdown(entry, content) {
269
+ return [
270
+ "---",
271
+ `id: ${entry.id}`,
272
+ `createdAt: ${entry.createdAt}`,
273
+ `source: ${entry.source}`,
274
+ `project-group: ${entry.projectGroup}`,
275
+ `type: ${entry.type}`,
276
+ `status: ${entry.status}`,
277
+ `relatedProjects: [${entry.relatedProjects.join(", ")}]`,
278
+ `tags: [${entry.tags.join(", ")}]`,
279
+ "---",
280
+ "",
281
+ `# ${entry.title}`,
282
+ "",
283
+ "## Summary",
284
+ "",
285
+ content.trim() || "(empty)",
286
+ "",
287
+ ].join("\n");
288
+ }
289
+ async function confirmSave(entry, content, opts) {
290
+ if (opts.yes || opts.json || !process.stdin.isTTY)
291
+ return true;
292
+ process.stdout.write(`Memory Book entry:\n`);
293
+ process.stdout.write(` title: ${entry.title}\n`);
294
+ process.stdout.write(` type: ${entry.type}\n`);
295
+ process.stdout.write(` group: ${entry.projectGroup}\n`);
296
+ process.stdout.write(` tags: ${entry.tags.join(", ") || "(none)"}\n`);
297
+ process.stdout.write(` text: ${truncate(content.trim().replace(/\s+/g, " "), 140)}\n`);
298
+ const rl = createInterface({ input, output });
299
+ const answer = await rl.question("Save to ~/.okstra/memory-book? [y/N] ");
300
+ rl.close();
301
+ return /^y(es)?$/i.test(answer.trim());
302
+ }
303
+ async function appendIndex(entry) {
304
+ await fs.mkdir(dirname(indexPath()), { recursive: true });
305
+ await fs.appendFile(indexPath(), JSON.stringify(entry) + "\n", "utf8");
306
+ }
307
+ function isMemoryEntry(value) {
308
+ if (!isRecord(value))
309
+ return false;
310
+ const record = value;
311
+ return (typeof record.id === "string" &&
312
+ typeof record.title === "string" &&
313
+ typeof record.type === "string" &&
314
+ isMemoryType(record.type) &&
315
+ typeof record.projectGroup === "string" &&
316
+ typeof record.source === "string" &&
317
+ Array.isArray(record.tags) &&
318
+ record.tags.every((tag) => typeof tag === "string") &&
319
+ Array.isArray(record.relatedProjects) &&
320
+ record.relatedProjects.every((project) => typeof project === "string") &&
321
+ (record.status === "active" || record.status === "archived") &&
322
+ typeof record.createdAt === "string" &&
323
+ (record.archivedAt === null || typeof record.archivedAt === "string") &&
324
+ typeof record.path === "string");
325
+ }
326
+ async function readIndex() {
327
+ let text;
328
+ try {
329
+ text = await fs.readFile(indexPath(), "utf8");
330
+ }
331
+ catch (err) {
332
+ if (isRecord(err) && err.code === "ENOENT")
333
+ return [];
334
+ throw err;
335
+ }
336
+ // Skip lines that fail to parse so one truncated row (e.g. an interrupted
337
+ // appendIndex) cannot make the whole Memory Book unreadable; other JSONL
338
+ // readers in okstra tolerate corruption the same way.
339
+ const entries = [];
340
+ for (const line of text.split(/\r?\n/)) {
341
+ if (!line)
342
+ continue;
343
+ try {
344
+ const parsed = JSON.parse(line);
345
+ if (isMemoryEntry(parsed))
346
+ entries.push(parsed);
347
+ }
348
+ catch {
349
+ // Drop the unparseable row; intact entries remain visible.
350
+ }
351
+ }
352
+ return entries;
353
+ }
354
+ async function writeIndex(entries) {
355
+ await fs.mkdir(dirname(indexPath()), { recursive: true });
356
+ const data = entries.map((entry) => JSON.stringify(entry)).join("\n");
357
+ await fs.writeFile(indexPath(), data ? `${data}\n` : "", "utf8");
358
+ }
359
+ function visibleEntries(entries, opts) {
360
+ return entries.filter((entry) => opts.includeArchived || entry.status !== "archived");
361
+ }
362
+ function formatEntryLine(entry) {
363
+ const tags = entry.tags.length > 0 ? ` #${entry.tags.join(" #")}` : "";
364
+ return `${entry.id} ${entry.createdAt.slice(0, 10)} ${entry.type} ${entry.title}${tags}`;
365
+ }
366
+ function emitJson(payload) {
367
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
368
+ }
369
+ async function opAdd(args) {
370
+ const opts = parseAddArgs(args);
371
+ const content = await readInputContent(opts);
372
+ const secrets = detectSecrets(content);
373
+ if (secrets.length > 0) {
374
+ throw new Error(`refusing to store likely secret(s): ${secrets.join(", ")}. ` +
375
+ "Memory Book must not hold credentials — redact the secret and store a note instead.");
376
+ }
377
+ const entry = buildEntry(opts, content, new Date());
378
+ if (!(await confirmSave(entry, content, opts))) {
379
+ process.stdout.write("cancelled\n");
380
+ return 0;
381
+ }
382
+ const absolutePath = join(memoryRoot(), entry.path);
383
+ await fs.mkdir(dirname(absolutePath), { recursive: true });
384
+ await fs.writeFile(absolutePath, renderMarkdown(entry, content), "utf8");
385
+ try {
386
+ await appendIndex(entry);
387
+ }
388
+ catch (err) {
389
+ // Index append failed after the file was written — remove the orphan so
390
+ // `add` stays all-or-nothing (an entry with no index row is invisible to
391
+ // list/search anyway).
392
+ await fs.rm(absolutePath, { force: true });
393
+ throw err;
394
+ }
395
+ if (opts.json)
396
+ emitJson({ ok: true, entry, path: absolutePath });
397
+ else
398
+ process.stdout.write(`saved ${entry.id}\n${absolutePath}\n`);
399
+ return 0;
400
+ }
401
+ async function opList(args) {
402
+ const opts = parseListArgs(args);
403
+ const entries = visibleEntries(await readIndex(), opts)
404
+ .filter((entry) => !opts.tag || entry.tags.includes(opts.tag))
405
+ .filter((entry) => !opts.type || entry.type === opts.type)
406
+ .filter((entry) => !opts.projectGroup || entryGroup(entry) === opts.projectGroup)
407
+ .slice(0, opts.limit);
408
+ if (opts.json)
409
+ emitJson(entries);
410
+ else
411
+ process.stdout.write(entries.map(formatEntryLine).join("\n") + (entries.length ? "\n" : ""));
412
+ return 0;
413
+ }
414
+ async function opSearch(args) {
415
+ const opts = parseQueryArgs(args);
416
+ const needle = opts.query.toLowerCase();
417
+ const entries = visibleEntries(await readIndex(), opts).filter((entry) => !opts.projectGroup || entryGroup(entry) === opts.projectGroup);
418
+ const matches = [];
419
+ for (const entry of entries) {
420
+ if (await entryMatches(entry, needle))
421
+ matches.push(entry);
422
+ if (matches.length >= opts.limit)
423
+ break;
424
+ }
425
+ if (opts.json)
426
+ emitJson(matches);
427
+ else
428
+ process.stdout.write(matches.map(formatEntryLine).join("\n") + (matches.length ? "\n" : ""));
429
+ return 0;
430
+ }
431
+ async function opGroups(args) {
432
+ const opts = parseGroupsArgs(args);
433
+ const counts = new Map();
434
+ for (const entry of visibleEntries(await readIndex(), opts)) {
435
+ const group = entryGroup(entry);
436
+ counts.set(group, (counts.get(group) ?? 0) + 1);
437
+ }
438
+ const groups = [...counts.entries()]
439
+ .map(([name, count]) => ({ name, count }))
440
+ .sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
441
+ if (opts.json)
442
+ emitJson(groups);
443
+ else
444
+ process.stdout.write(groups.map((g) => `${g.name} (${g.count})`).join("\n") + (groups.length ? "\n" : ""));
445
+ return 0;
446
+ }
447
+ async function entryMatches(entry, needle) {
448
+ const haystack = [
449
+ entry.id,
450
+ entry.title,
451
+ entry.type,
452
+ entryGroup(entry),
453
+ entry.source,
454
+ ...entry.tags,
455
+ ...entry.relatedProjects,
456
+ ]
457
+ .join(" ")
458
+ .toLowerCase();
459
+ if (haystack.includes(needle))
460
+ return true;
461
+ try {
462
+ const body = await fs.readFile(join(memoryRoot(), entry.path), "utf8");
463
+ return body.toLowerCase().includes(needle);
464
+ }
465
+ catch {
466
+ return false;
467
+ }
468
+ }
469
+ async function findEntry(id) {
470
+ const entries = await readIndex();
471
+ const entry = entries.find((candidate) => candidate.id === id);
472
+ if (!entry)
473
+ throw new Error(`memory entry not found: ${id}`);
474
+ return { entry, entries };
475
+ }
476
+ async function opShow(args) {
477
+ const json = args.includes("--json");
478
+ const ids = args.filter((arg) => arg !== "--json");
479
+ if (ids.length !== 1)
480
+ throw new Error("show requires exactly one id");
481
+ const id = ids[0];
482
+ if (id === undefined)
483
+ throw new Error("show requires exactly one id");
484
+ const { entry } = await findEntry(id);
485
+ const absolutePath = join(memoryRoot(), entry.path);
486
+ const body = await fs.readFile(absolutePath, "utf8");
487
+ if (json)
488
+ emitJson({ entry, path: absolutePath, body });
489
+ else
490
+ process.stdout.write(body);
491
+ return 0;
492
+ }
493
+ async function opArchive(args) {
494
+ const json = args.includes("--json");
495
+ const ids = args.filter((arg) => arg !== "--json");
496
+ if (ids.length !== 1)
497
+ throw new Error("archive requires exactly one id");
498
+ const id = ids[0];
499
+ if (id === undefined)
500
+ throw new Error("archive requires exactly one id");
501
+ const { entry, entries } = await findEntry(id);
502
+ const archivedEntry = await moveToArchive(entry);
503
+ const updated = entries.map((candidate) => candidate.id === entry.id ? archivedEntry : candidate);
504
+ try {
505
+ await writeIndex(updated);
506
+ }
507
+ catch (err) {
508
+ // Index rewrite failed after the file was already moved — move it back so
509
+ // the index keeps pointing at the file's real location (mirrors opAdd).
510
+ if (archivedEntry.path !== entry.path) {
511
+ await fs.rename(join(memoryRoot(), archivedEntry.path), join(memoryRoot(), entry.path));
512
+ }
513
+ throw err;
514
+ }
515
+ if (json)
516
+ emitJson({ ok: true, entry: archivedEntry });
517
+ else
518
+ process.stdout.write(`archived ${entry.id}\n`);
519
+ return 0;
520
+ }
521
+ async function moveToArchive(entry) {
522
+ if (entry.status === "archived")
523
+ return entry;
524
+ const sourcePath = join(memoryRoot(), entry.path);
525
+ const archiveDir = join("archive", entry.createdAt.slice(0, 4));
526
+ const nextPath = join(archiveDir, basename(entry.path));
527
+ const targetPath = join(memoryRoot(), nextPath);
528
+ await fs.mkdir(dirname(targetPath), { recursive: true });
529
+ await fs.rename(sourcePath, targetPath);
530
+ return {
531
+ ...entry,
532
+ status: "archived",
533
+ archivedAt: new Date().toISOString(),
534
+ path: relative(memoryRoot(), targetPath),
535
+ };
536
+ }
537
+ export async function run(args) {
538
+ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
539
+ process.stdout.write(USAGE);
540
+ return args.length === 0 ? 2 : 0;
541
+ }
542
+ const op = args[0];
543
+ const rest = args.slice(1);
544
+ try {
545
+ switch (op) {
546
+ case "add":
547
+ return await opAdd(rest);
548
+ case "list":
549
+ return await opList(rest);
550
+ case "search":
551
+ return await opSearch(rest);
552
+ case "groups":
553
+ return await opGroups(rest);
554
+ case "show":
555
+ return await opShow(rest);
556
+ case "archive":
557
+ return await opArchive(rest);
558
+ default:
559
+ process.stderr.write(`unknown memory subcommand: ${op}\n\n${USAGE}`);
560
+ return 2;
561
+ }
562
+ }
563
+ catch (err) {
564
+ const message = err instanceof Error ? err.message : String(err);
565
+ process.stderr.write(`error: ${message}\n`);
566
+ return 1;
567
+ }
568
+ }
569
+ //# sourceMappingURL=memory.mjs.map