@latysh-development/monolith 0.0.0-stage → 0.1.0-alpha.1

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 (345) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +179 -2
  3. package/dist/brief/cli.d.ts +30 -0
  4. package/dist/brief/cli.d.ts.map +1 -0
  5. package/dist/brief/cli.js +85 -0
  6. package/dist/brief/collect.d.ts +17 -0
  7. package/dist/brief/collect.d.ts.map +1 -0
  8. package/dist/brief/collect.js +342 -0
  9. package/dist/brief/config.d.ts +40 -0
  10. package/dist/brief/config.d.ts.map +1 -0
  11. package/dist/brief/config.js +85 -0
  12. package/dist/brief/constants.d.ts +12 -0
  13. package/dist/brief/constants.d.ts.map +1 -0
  14. package/dist/brief/constants.js +11 -0
  15. package/dist/brief/docs.d.ts +53 -0
  16. package/dist/brief/docs.d.ts.map +1 -0
  17. package/dist/brief/docs.js +229 -0
  18. package/dist/brief/freshness.d.ts +104 -0
  19. package/dist/brief/freshness.d.ts.map +1 -0
  20. package/dist/brief/freshness.js +263 -0
  21. package/dist/brief/git-signals.d.ts +17 -0
  22. package/dist/brief/git-signals.d.ts.map +1 -0
  23. package/dist/brief/git-signals.js +101 -0
  24. package/dist/brief/index.d.ts +11 -0
  25. package/dist/brief/index.d.ts.map +1 -0
  26. package/dist/brief/index.js +5 -0
  27. package/dist/brief/reconcile.d.ts +75 -0
  28. package/dist/brief/reconcile.d.ts.map +1 -0
  29. package/dist/brief/reconcile.js +259 -0
  30. package/dist/brief/render.d.ts +67 -0
  31. package/dist/brief/render.d.ts.map +1 -0
  32. package/dist/brief/render.js +458 -0
  33. package/dist/brief/sanitize.d.ts +38 -0
  34. package/dist/brief/sanitize.d.ts.map +1 -0
  35. package/dist/brief/sanitize.js +48 -0
  36. package/dist/brief/selectors.d.ts +36 -0
  37. package/dist/brief/selectors.d.ts.map +1 -0
  38. package/dist/brief/selectors.js +75 -0
  39. package/dist/brief/story.d.ts +18 -0
  40. package/dist/brief/story.d.ts.map +1 -0
  41. package/dist/brief/story.js +59 -0
  42. package/dist/brief/types.d.ts +126 -0
  43. package/dist/brief/types.d.ts.map +1 -0
  44. package/dist/brief/types.js +11 -0
  45. package/dist/cli/index.d.ts +3 -0
  46. package/dist/cli/index.d.ts.map +1 -0
  47. package/dist/cli/index.js +199 -0
  48. package/dist/cli/init-args.d.ts +16 -0
  49. package/dist/cli/init-args.d.ts.map +1 -0
  50. package/dist/cli/init-args.js +24 -0
  51. package/dist/cli/ui-args.d.ts +23 -0
  52. package/dist/cli/ui-args.d.ts.map +1 -0
  53. package/dist/cli/ui-args.js +66 -0
  54. package/dist/cli/ui.d.ts +40 -0
  55. package/dist/cli/ui.d.ts.map +1 -0
  56. package/dist/cli/ui.js +180 -0
  57. package/dist/coordinatorium/checkpoint.d.ts +44 -0
  58. package/dist/coordinatorium/checkpoint.d.ts.map +1 -0
  59. package/dist/coordinatorium/checkpoint.js +124 -0
  60. package/dist/coordinatorium/cli.d.ts +39 -0
  61. package/dist/coordinatorium/cli.d.ts.map +1 -0
  62. package/dist/coordinatorium/cli.js +203 -0
  63. package/dist/coordinatorium/errors.d.ts +112 -0
  64. package/dist/coordinatorium/errors.d.ts.map +1 -0
  65. package/dist/coordinatorium/errors.js +145 -0
  66. package/dist/coordinatorium/index.d.ts +11 -0
  67. package/dist/coordinatorium/index.d.ts.map +1 -0
  68. package/dist/coordinatorium/index.js +5 -0
  69. package/dist/coordinatorium/lock.d.ts +187 -0
  70. package/dist/coordinatorium/lock.d.ts.map +1 -0
  71. package/dist/coordinatorium/lock.js +659 -0
  72. package/dist/coordinatorium/mission.d.ts +32 -0
  73. package/dist/coordinatorium/mission.d.ts.map +1 -0
  74. package/dist/coordinatorium/mission.js +87 -0
  75. package/dist/coordinatorium/state-keeper.d.ts +115 -0
  76. package/dist/coordinatorium/state-keeper.d.ts.map +1 -0
  77. package/dist/coordinatorium/state-keeper.js +396 -0
  78. package/dist/coordinatorium/types.d.ts +69 -0
  79. package/dist/coordinatorium/types.d.ts.map +1 -0
  80. package/dist/coordinatorium/types.js +27 -0
  81. package/dist/discovery/compose.d.ts +28 -0
  82. package/dist/discovery/compose.d.ts.map +1 -0
  83. package/dist/discovery/compose.js +159 -0
  84. package/dist/discovery/contracts.d.ts +211 -0
  85. package/dist/discovery/contracts.d.ts.map +1 -0
  86. package/dist/discovery/contracts.js +555 -0
  87. package/dist/discovery/docs.d.ts +278 -0
  88. package/dist/discovery/docs.d.ts.map +1 -0
  89. package/dist/discovery/docs.js +1182 -0
  90. package/dist/discovery/facts.d.ts +49 -0
  91. package/dist/discovery/facts.d.ts.map +1 -0
  92. package/dist/discovery/facts.js +541 -0
  93. package/dist/discovery/index.d.ts +8 -0
  94. package/dist/discovery/index.d.ts.map +1 -0
  95. package/dist/discovery/index.js +9 -0
  96. package/dist/discovery/orchestrate.d.ts +35 -0
  97. package/dist/discovery/orchestrate.d.ts.map +1 -0
  98. package/dist/discovery/orchestrate.js +32 -0
  99. package/dist/discovery/render.d.ts +36 -0
  100. package/dist/discovery/render.d.ts.map +1 -0
  101. package/dist/discovery/render.js +278 -0
  102. package/dist/discovery/traverse.d.ts +197 -0
  103. package/dist/discovery/traverse.d.ts.map +1 -0
  104. package/dist/discovery/traverse.js +510 -0
  105. package/dist/durable/assess.d.ts +105 -0
  106. package/dist/durable/assess.d.ts.map +1 -0
  107. package/dist/durable/assess.js +239 -0
  108. package/dist/durable/canonical.d.ts +25 -0
  109. package/dist/durable/canonical.d.ts.map +1 -0
  110. package/dist/durable/canonical.js +78 -0
  111. package/dist/durable/cli.d.ts +37 -0
  112. package/dist/durable/cli.d.ts.map +1 -0
  113. package/dist/durable/cli.js +141 -0
  114. package/dist/durable/errors.d.ts +192 -0
  115. package/dist/durable/errors.d.ts.map +1 -0
  116. package/dist/durable/errors.js +323 -0
  117. package/dist/durable/fs-store.d.ts +254 -0
  118. package/dist/durable/fs-store.d.ts.map +1 -0
  119. package/dist/durable/fs-store.js +1493 -0
  120. package/dist/durable/index.d.ts +20 -0
  121. package/dist/durable/index.d.ts.map +1 -0
  122. package/dist/durable/index.js +13 -0
  123. package/dist/durable/log.d.ts +33 -0
  124. package/dist/durable/log.d.ts.map +1 -0
  125. package/dist/durable/log.js +157 -0
  126. package/dist/durable/memory-store.d.ts +51 -0
  127. package/dist/durable/memory-store.d.ts.map +1 -0
  128. package/dist/durable/memory-store.js +302 -0
  129. package/dist/durable/projection.d.ts +35 -0
  130. package/dist/durable/projection.d.ts.map +1 -0
  131. package/dist/durable/projection.js +328 -0
  132. package/dist/durable/prune-check.d.ts +34 -0
  133. package/dist/durable/prune-check.d.ts.map +1 -0
  134. package/dist/durable/prune-check.js +72 -0
  135. package/dist/durable/register.d.ts +100 -0
  136. package/dist/durable/register.d.ts.map +1 -0
  137. package/dist/durable/register.js +532 -0
  138. package/dist/durable/retention.d.ts +49 -0
  139. package/dist/durable/retention.d.ts.map +1 -0
  140. package/dist/durable/retention.js +59 -0
  141. package/dist/durable/runtime.d.ts +499 -0
  142. package/dist/durable/runtime.d.ts.map +1 -0
  143. package/dist/durable/runtime.js +1288 -0
  144. package/dist/durable/seams.d.ts +44 -0
  145. package/dist/durable/seams.d.ts.map +1 -0
  146. package/dist/durable/seams.js +21 -0
  147. package/dist/durable/testing.d.ts +11 -0
  148. package/dist/durable/testing.d.ts.map +1 -0
  149. package/dist/durable/testing.js +25 -0
  150. package/dist/durable/types.d.ts +442 -0
  151. package/dist/durable/types.d.ts.map +1 -0
  152. package/dist/durable/types.js +75 -0
  153. package/dist/graph/builder.d.ts +11 -0
  154. package/dist/graph/builder.d.ts.map +1 -0
  155. package/dist/graph/builder.js +153 -0
  156. package/dist/graph/identity.d.ts +37 -0
  157. package/dist/graph/identity.d.ts.map +1 -0
  158. package/dist/graph/identity.js +46 -0
  159. package/dist/graph/index.d.ts +7 -0
  160. package/dist/graph/index.d.ts.map +1 -0
  161. package/dist/graph/index.js +3 -0
  162. package/dist/graph/resolver.d.ts +41 -0
  163. package/dist/graph/resolver.d.ts.map +1 -0
  164. package/dist/graph/resolver.js +36 -0
  165. package/dist/graph/types.d.ts +42 -0
  166. package/dist/graph/types.d.ts.map +1 -0
  167. package/dist/graph/types.js +1 -0
  168. package/dist/http/index.d.ts +5 -0
  169. package/dist/http/index.d.ts.map +1 -0
  170. package/dist/http/index.js +2 -0
  171. package/dist/http/server.d.ts +76 -0
  172. package/dist/http/server.d.ts.map +1 -0
  173. package/dist/http/server.js +195 -0
  174. package/dist/http/ui-assets.d.ts +22 -0
  175. package/dist/http/ui-assets.d.ts.map +1 -0
  176. package/dist/http/ui-assets.js +100 -0
  177. package/dist/ideas/cli.d.ts +33 -0
  178. package/dist/ideas/cli.d.ts.map +1 -0
  179. package/dist/ideas/cli.js +158 -0
  180. package/dist/ideas/format.d.ts +38 -0
  181. package/dist/ideas/format.d.ts.map +1 -0
  182. package/dist/ideas/format.js +194 -0
  183. package/dist/ideas/index.d.ts +6 -0
  184. package/dist/ideas/index.d.ts.map +1 -0
  185. package/dist/ideas/index.js +3 -0
  186. package/dist/ideas/store.d.ts +55 -0
  187. package/dist/ideas/store.d.ts.map +1 -0
  188. package/dist/ideas/store.js +262 -0
  189. package/dist/ideas/types.d.ts +54 -0
  190. package/dist/ideas/types.d.ts.map +1 -0
  191. package/dist/ideas/types.js +26 -0
  192. package/dist/index.d.ts +32 -0
  193. package/dist/index.d.ts.map +1 -0
  194. package/dist/index.js +28 -0
  195. package/dist/init/index.d.ts +12 -0
  196. package/dist/init/index.d.ts.map +1 -0
  197. package/dist/init/index.js +90 -0
  198. package/dist/init/prompts.d.ts +3 -0
  199. package/dist/init/prompts.d.ts.map +1 -0
  200. package/dist/init/prompts.js +47 -0
  201. package/dist/init/scaffold.d.ts +29 -0
  202. package/dist/init/scaffold.d.ts.map +1 -0
  203. package/dist/init/scaffold.js +208 -0
  204. package/dist/init/types.d.ts +14 -0
  205. package/dist/init/types.d.ts.map +1 -0
  206. package/dist/init/types.js +1 -0
  207. package/dist/managed/index.d.ts +73 -0
  208. package/dist/managed/index.d.ts.map +1 -0
  209. package/dist/managed/index.js +222 -0
  210. package/dist/readmodel/dashboard.d.ts +40 -0
  211. package/dist/readmodel/dashboard.d.ts.map +1 -0
  212. package/dist/readmodel/dashboard.js +236 -0
  213. package/dist/readmodel/index.d.ts +3 -0
  214. package/dist/readmodel/index.d.ts.map +1 -0
  215. package/dist/readmodel/index.js +1 -0
  216. package/dist/readmodel/types.d.ts +104 -0
  217. package/dist/readmodel/types.d.ts.map +1 -0
  218. package/dist/readmodel/types.js +15 -0
  219. package/dist/root/index.d.ts +186 -0
  220. package/dist/root/index.d.ts.map +1 -0
  221. package/dist/root/index.js +430 -0
  222. package/dist/root/safe-write.d.ts +268 -0
  223. package/dist/root/safe-write.d.ts.map +1 -0
  224. package/dist/root/safe-write.js +966 -0
  225. package/dist/root/write-artifacts.d.ts +20 -0
  226. package/dist/root/write-artifacts.d.ts.map +1 -0
  227. package/dist/root/write-artifacts.js +42 -0
  228. package/dist/scriptorium/compile.d.ts +4 -0
  229. package/dist/scriptorium/compile.d.ts.map +1 -0
  230. package/dist/scriptorium/compile.js +185 -0
  231. package/dist/scriptorium/frontmatter.d.ts +13 -0
  232. package/dist/scriptorium/frontmatter.d.ts.map +1 -0
  233. package/dist/scriptorium/frontmatter.js +90 -0
  234. package/dist/scriptorium/index.d.ts +3 -0
  235. package/dist/scriptorium/index.d.ts.map +1 -0
  236. package/dist/scriptorium/index.js +1 -0
  237. package/dist/scriptorium/markdown.d.ts +31 -0
  238. package/dist/scriptorium/markdown.d.ts.map +1 -0
  239. package/dist/scriptorium/markdown.js +88 -0
  240. package/dist/scriptorium/types.d.ts +78 -0
  241. package/dist/scriptorium/types.d.ts.map +1 -0
  242. package/dist/scriptorium/types.js +1 -0
  243. package/dist/types/enforcement.d.ts +11 -0
  244. package/dist/types/enforcement.d.ts.map +1 -0
  245. package/dist/types/enforcement.js +1 -0
  246. package/dist/types/integrity.d.ts +33 -0
  247. package/dist/types/integrity.d.ts.map +1 -0
  248. package/dist/types/integrity.js +1 -0
  249. package/dist/types/project-definition.d.ts +30 -0
  250. package/dist/types/project-definition.d.ts.map +1 -0
  251. package/dist/types/project-definition.js +1 -0
  252. package/dist/ui/assets/cosmic-glass-bg-Dwx47zPu.webp +0 -0
  253. package/dist/ui/assets/index-C4NhMaq7.js +28 -0
  254. package/dist/ui/assets/index-DMZ7ZvZY.css +1 -0
  255. package/dist/ui/index.html +13 -0
  256. package/dist/update/cli.d.ts +8 -0
  257. package/dist/update/cli.d.ts.map +1 -0
  258. package/dist/update/cli.js +115 -0
  259. package/dist/update/index.d.ts +78 -0
  260. package/dist/update/index.d.ts.map +1 -0
  261. package/dist/update/index.js +285 -0
  262. package/dist/validate/index.d.ts +6 -0
  263. package/dist/validate/index.d.ts.map +1 -0
  264. package/dist/validate/index.js +214 -0
  265. package/dist/version.d.ts +2 -0
  266. package/dist/version.d.ts.map +1 -0
  267. package/dist/version.js +1 -0
  268. package/package.json +67 -4
  269. package/templates/.gitkeep +0 -0
  270. package/templates/default-instance/instruction/.cursorrules +59 -0
  271. package/templates/default-instance/instruction/.windsurfrules +59 -0
  272. package/templates/default-instance/instruction/CLAUDE.md +57 -0
  273. package/templates/default-instance/managed/agent.md +20 -0
  274. package/templates/default-instance/managed/bootstrap.md +7 -0
  275. package/templates/default-instance/managed/index-idea.md +5 -0
  276. package/templates/default-instance/managed/routing-idea.md +14 -0
  277. package/templates/default-instance/monolith/AGENT.md +1 -0
  278. package/templates/default-instance/monolith/context/boot/HANDOFF.md +49 -0
  279. package/templates/default-instance/monolith/context/boot/WORK_SESSION.md +30 -0
  280. package/templates/default-instance/monolith/context/execution/current-state.md +30 -0
  281. package/templates/default-instance/monolith/context/maps/dependency-map.md +38 -0
  282. package/templates/default-instance/monolith/context/maps/project-map.md +85 -0
  283. package/templates/default-instance/monolith/context/maps/roadmap-index.md +41 -0
  284. package/templates/default-instance/monolith/context/maps/routing-map.md +151 -0
  285. package/templates/default-instance/monolith/context/maps/semantic-map.md +75 -0
  286. package/templates/default-instance/monolith/context/models/model-registry.md +37 -0
  287. package/templates/default-instance/monolith/context/models/privacy-classification.md +35 -0
  288. package/templates/default-instance/monolith/context/templates/plan-template.md +56 -0
  289. package/templates/default-instance/monolith/governance/README.md +29 -0
  290. package/templates/default-instance/monolith/governance/RULE_SCHEMA.md +58 -0
  291. package/templates/default-instance/monolith/learning/INDEX.md +48 -0
  292. package/templates/default-instance/monolith/learning/injection-map.md +49 -0
  293. package/templates/default-instance/monolith/learning/taxonomy.md +30 -0
  294. package/templates/default-instance/monolith/memory/errors/README.md +26 -0
  295. package/templates/default-instance/monolith/memory/errors/SCHEMA.md +47 -0
  296. package/templates/default-instance/monolith/memory/errors/TAXONOMY.md +27 -0
  297. package/templates/default-instance/monolith/memory/long-term/architecture-principles.md +31 -0
  298. package/templates/default-instance/monolith/memory/long-term/decisions.md +36 -0
  299. package/templates/default-instance/monolith/memory/long-term/execution-history.md +20 -0
  300. package/templates/default-instance/monolith/memory/long-term/tech-debt.md +25 -0
  301. package/templates/default-instance/monolith/memory/working/active.md +41 -0
  302. package/templates/default-instance/monolith/memory/working/governance-result.md +0 -0
  303. package/templates/default-instance/monolith/memory/working/integrity-result.md +0 -0
  304. package/templates/default-instance/monolith/memory/working/story-index.md +28 -0
  305. package/templates/default-instance/monolith/protocols/boot/boot-sequence.md +59 -0
  306. package/templates/default-instance/monolith/protocols/cognition/cognitive-modes.md +90 -0
  307. package/templates/default-instance/monolith/protocols/cognition/dry-run.md +56 -0
  308. package/templates/default-instance/monolith/protocols/cognition/execution-trace-format.md +52 -0
  309. package/templates/default-instance/monolith/protocols/cognition/semantic-planning.md +51 -0
  310. package/templates/default-instance/monolith/protocols/execution/hybrid-execution.md +37 -0
  311. package/templates/default-instance/monolith/protocols/execution/local-llm-governance.md +42 -0
  312. package/templates/default-instance/monolith/protocols/governance/command-policy.md +51 -0
  313. package/templates/default-instance/monolith/protocols/governance/core-invariants.md +40 -0
  314. package/templates/default-instance/monolith/protocols/governance/memory-governance.md +54 -0
  315. package/templates/default-instance/monolith/protocols/governance/metadata-schema.md +62 -0
  316. package/templates/default-instance/monolith/protocols/governance/skills.md +53 -0
  317. package/templates/default-instance/monolith/protocols/governance/work-session.md +46 -0
  318. package/templates/default-instance/monolith/skills/INDEX.md +32 -0
  319. package/templates/default-instance/monolith/skills/current-status/SKILL.md +47 -0
  320. package/templates/default-instance/monolith/skills/finalize-story/SKILL.md +86 -0
  321. package/templates/default-instance/monolith/skills/global-status/SKILL.md +56 -0
  322. package/templates/default-instance/monolith/skills/idea-capture/SKILL.md +157 -0
  323. package/templates/default-instance/monolith/skills/self-improvement/SKILL.md +77 -0
  324. package/templates/default-instance/monolith/state.json +11 -0
  325. package/templates/default-instance/monolith/tests/INDEX.md +57 -0
  326. package/templates/default-instance/monolith/tests/scenarios/architecture-review.md +32 -0
  327. package/templates/default-instance/monolith/tests/scenarios/debug-failure.md +32 -0
  328. package/templates/default-instance/monolith/tests/scenarios/dry-run-activation.md +33 -0
  329. package/templates/default-instance/monolith/tests/scenarios/execution-trace-verification.md +31 -0
  330. package/templates/default-instance/monolith/tests/scenarios/finalize-story.md +35 -0
  331. package/templates/default-instance/monolith/tests/scenarios/implement-feature.md +36 -0
  332. package/templates/default-instance/monolith/tests/scenarios/local-llm-delegation.md +31 -0
  333. package/templates/default-instance/monolith/tests/scenarios/planning-request.md +32 -0
  334. package/templates/default-instance/monolith/tests/scenarios/sensitive-context-task.md +30 -0
  335. package/templates/default-instance/monolith/tests/scenarios/validator-verification.md +33 -0
  336. package/templates/default-instance/monolith/tests/validation/expected-behavior.md +70 -0
  337. package/templates/default-instance/monolith/tests/validators/INDEX.md +17 -0
  338. package/templates/default-instance/monolith/tests/validators/delegation-semantics.md +22 -0
  339. package/templates/default-instance/monolith/tests/validators/mode-consistency.md +25 -0
  340. package/templates/default-instance/monolith/tests/validators/privacy-routing.md +23 -0
  341. package/templates/default-instance/monolith/tests/validators/protocol-references.md +27 -0
  342. package/templates/default-instance/monolith/tests/validators/routing-integrity.md +30 -0
  343. package/templates/default-instance/projectroot/AGENTS.md +111 -0
  344. package/templates/default-instance/projectroot/monolith.config.json +32 -0
  345. package/templates/default-instance/projectroot/project.definition.mjs.template +41 -0
package/dist/cli/ui.js ADDED
@@ -0,0 +1,180 @@
1
+ /* eslint-disable no-console */
2
+ /**
3
+ * `monolith ui` — launches Mission Control (ADR-010) for the project in the
4
+ * current directory.
5
+ *
6
+ * monolith ui production: the built UI (dist/ui) and the read-only API on
7
+ * one loopback port. No Vite, no devDependencies.
8
+ * monolith ui dev development: the same API on a free loopback port plus a
9
+ * Vite dev server (HMR) that proxies /api/dashboard to it.
10
+ *
11
+ * Nothing here writes to the project, runs project code or opens a browser.
12
+ * Expected user errors are one line on stderr and exit code 1, never a stack.
13
+ */
14
+ import { spawn } from 'child_process';
15
+ import { existsSync, lstatSync } from 'fs';
16
+ import { dirname, join, resolve } from 'path';
17
+ import { fileURLToPath } from 'url';
18
+ import { startDashboardServer, loadUiAssets, UiAssetsError } from '../http/index.js';
19
+ import { RootContainmentError, resolveMonolithRoot } from '../root/index.js';
20
+ import { DEFAULT_DEV_UI_PORT, DEFAULT_UI_PORT, UI_DEV_USAGE, UI_USAGE, parseUiArgs } from './ui-args.js';
21
+ /** Thrown for expected, user-fixable failures. */
22
+ export class UiLaunchError extends Error {
23
+ }
24
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
25
+ const DEFAULT_UI_DIR = resolve(moduleDir, '..', 'ui');
26
+ const DEFAULT_PACKAGE_ROOT = resolve(moduleDir, '..', '..');
27
+ /** The project must be a MONOLITH instance: contained `monolithRoot` that exists as a real directory. */
28
+ export function assertMonolithProject(projectRoot) {
29
+ let rootAbs;
30
+ try {
31
+ rootAbs = resolveMonolithRoot(projectRoot).rootAbs;
32
+ }
33
+ catch (err) {
34
+ if (err instanceof RootContainmentError) {
35
+ throw new UiLaunchError(`Not a usable MONOLITH project: ${err.message}`);
36
+ }
37
+ throw new UiLaunchError(`Cannot inspect the project at ${projectRoot}.`);
38
+ }
39
+ let isDir = false;
40
+ try {
41
+ isDir = lstatSync(rootAbs).isDirectory();
42
+ }
43
+ catch {
44
+ // reported below
45
+ }
46
+ if (!isDir) {
47
+ throw new UiLaunchError(`No MONOLITH instance found in ${projectRoot} (expected directory ${rootAbs}). Run 'monolith init' here, or cd into a MONOLITH project.`);
48
+ }
49
+ }
50
+ async function listen(projectRoot, port, assets) {
51
+ try {
52
+ return await startDashboardServer({ projectRoot, port, assets });
53
+ }
54
+ catch (err) {
55
+ const code = err.code;
56
+ if (code === 'EADDRINUSE') {
57
+ throw new UiLaunchError(`Port ${port} is already in use. Choose another with --port N.`);
58
+ }
59
+ if (code === 'EACCES') {
60
+ throw new UiLaunchError(`Not permitted to listen on port ${port}. Choose another with --port N.`);
61
+ }
62
+ throw new UiLaunchError(`Could not start the server: ${err instanceof Error ? err.message : String(err)}`);
63
+ }
64
+ }
65
+ export async function launchUi(opts) {
66
+ const projectRoot = resolve(opts.projectRoot ?? process.cwd());
67
+ const log = opts.log ?? ((line) => console.log(line));
68
+ assertMonolithProject(projectRoot);
69
+ let assets;
70
+ try {
71
+ assets = loadUiAssets(opts.uiDir ?? DEFAULT_UI_DIR);
72
+ }
73
+ catch (err) {
74
+ if (err instanceof UiAssetsError) {
75
+ throw new UiLaunchError(`Mission Control UI build is not usable: ${err.message} Reinstall the package, or run 'pnpm build:ui' in a source checkout.`);
76
+ }
77
+ throw err;
78
+ }
79
+ const server = await listen(projectRoot, opts.port, assets);
80
+ log(`MONOLITH Mission Control: ${server.url}`);
81
+ log(`Project: ${projectRoot}`);
82
+ log('Read-only. Listening on 127.0.0.1 only. Press Ctrl+C to stop.');
83
+ return {
84
+ url: server.url,
85
+ stop: () => server.close(),
86
+ ended: new Promise(() => undefined),
87
+ };
88
+ }
89
+ export async function launchUiDev(opts) {
90
+ const projectRoot = resolve(opts.projectRoot ?? process.cwd());
91
+ const packageRoot = opts.packageRoot ?? DEFAULT_PACKAGE_ROOT;
92
+ const log = opts.log ?? ((line) => console.log(line));
93
+ assertMonolithProject(projectRoot);
94
+ const viteBin = join(packageRoot, 'node_modules', 'vite', 'bin', 'vite.js');
95
+ const viteConfig = join(packageRoot, 'ui', 'vite.config.ts');
96
+ if (!existsSync(viteConfig) || !existsSync(viteBin)) {
97
+ throw new UiLaunchError("'monolith ui dev' needs a source checkout of MONOLITH with devDependencies installed (ui/ and vite were not found). Use 'monolith ui' for the built interface.");
98
+ }
99
+ // The API takes any free port; Vite is told where it is through the env var its config reads.
100
+ const api = await listen(projectRoot, 0);
101
+ const child = spawn(process.execPath, [viteBin, '--config', viteConfig, '--host', '127.0.0.1', '--port', String(opts.port), '--strictPort'], { stdio: 'inherit', env: { ...process.env, MONOLITH_API_PORT: String(api.port) } });
102
+ let childExited = false;
103
+ const exited = new Promise((resolveExit) => {
104
+ child.once('exit', (code, signal) => {
105
+ childExited = true;
106
+ resolveExit(code ?? (signal === null ? 1 : 0));
107
+ });
108
+ child.once('error', () => {
109
+ childExited = true;
110
+ resolveExit(1);
111
+ });
112
+ });
113
+ let stopping = null;
114
+ const stop = () => {
115
+ stopping ??= (async () => {
116
+ if (!childExited)
117
+ child.kill('SIGTERM');
118
+ await exited;
119
+ await api.close();
120
+ })();
121
+ return stopping;
122
+ };
123
+ const ended = exited.then(async (code) => {
124
+ await stop();
125
+ return code;
126
+ });
127
+ log(`MONOLITH Mission Control (dev, HMR): http://127.0.0.1:${opts.port}`);
128
+ log(`Project: ${projectRoot} (dashboard API on ${api.url}, read-only)`);
129
+ log('Listening on 127.0.0.1 only. Press Ctrl+C to stop.');
130
+ return { url: `http://127.0.0.1:${opts.port}`, stop, ended };
131
+ }
132
+ /** CLI entry for `monolith ui ...`. Resolves with the process exit code once the launch has ended. */
133
+ export async function runUi(args) {
134
+ const parsed = parseUiArgs(args);
135
+ if (!parsed.ok) {
136
+ console.error(`${parsed.message}\n\n${UI_USAGE}`);
137
+ return 1;
138
+ }
139
+ if (parsed.help) {
140
+ console.log(parsed.dev ? UI_DEV_USAGE : UI_USAGE);
141
+ return 0;
142
+ }
143
+ // Handlers go in before anything starts listening, so a signal that arrives right after the
144
+ // "listening" line can never hit the default action (killed by signal, exit code null).
145
+ let handle = null;
146
+ let signalled = false;
147
+ let finish = () => undefined;
148
+ const result = new Promise((resolveCode) => {
149
+ let done = false;
150
+ finish = (code) => {
151
+ if (done)
152
+ return;
153
+ done = true;
154
+ resolveCode(code);
155
+ };
156
+ });
157
+ const onSignal = () => {
158
+ signalled = true;
159
+ if (handle !== null)
160
+ void handle.stop().then(() => finish(0), () => finish(1));
161
+ };
162
+ process.once('SIGINT', onSignal);
163
+ process.once('SIGTERM', onSignal);
164
+ try {
165
+ handle = parsed.dev
166
+ ? await launchUiDev({ port: parsed.port ?? DEFAULT_DEV_UI_PORT })
167
+ : await launchUi({ port: parsed.port ?? DEFAULT_UI_PORT });
168
+ }
169
+ catch (err) {
170
+ if (err instanceof UiLaunchError) {
171
+ console.error(err.message);
172
+ return 1;
173
+ }
174
+ throw err;
175
+ }
176
+ void handle.ended.then(finish);
177
+ if (signalled)
178
+ onSignal();
179
+ return result;
180
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Tactical Coordinatorium — Mission Checkpoints (COORDINATORIUM-002).
3
+ *
4
+ * A Checkpoint is a durable, append-only progress note belonging to exactly
5
+ * one Mission. This module owns checkpoint semantics (ID generation,
6
+ * validation, the "only an active mission may be checkpointed" rule) —
7
+ * state-keeper.ts owns persistence and schema shape, exactly mirroring the
8
+ * mission.ts / state-keeper.ts split established in ADR-008.
9
+ *
10
+ * API surface is deliberately append-only: `createCheckpoint`,
11
+ * `listCheckpoints`, `getCheckpoint`. There is no update or delete path —
12
+ * once written, a checkpoint record never changes.
13
+ */
14
+ import { type StateReadTarget, type StateWriteTarget } from './state-keeper.js';
15
+ import type { Checkpoint } from './types.js';
16
+ export interface CreateCheckpointInput {
17
+ summary: string;
18
+ remainingSteps?: string[];
19
+ context?: string | null;
20
+ }
21
+ /**
22
+ * Append a checkpoint to an existing, in_progress mission. Validation
23
+ * happens before the lock is taken (pure input checks); the
24
+ * mission-existence check, the active-status check, and the ID derivation
25
+ * all happen inside the same mutateState critical section as the write, so
26
+ * two concurrent calls for the same mission can never collide on an ID or
27
+ * lose an update — a direct consequence of reusing mutateState, not new
28
+ * concurrency design.
29
+ */
30
+ export declare function createCheckpoint(target: StateWriteTarget, missionId: string, input: CreateCheckpointInput): Promise<Checkpoint>;
31
+ /**
32
+ * List a mission's checkpoints, oldest first. Never throws for an unknown
33
+ * or checkpoint-less mission — returns `[]`, consistent with a purely
34
+ * read-side filter over tactical.checkpoints (mirrors listMissions's
35
+ * "no missions yet" behavior). Remains available for a finished mission —
36
+ * only *creation* is gated on mission status.
37
+ */
38
+ export declare function listCheckpoints(target: StateReadTarget, missionId: string): Promise<Checkpoint[]>;
39
+ /**
40
+ * Fetch a single checkpoint by id, regardless of its mission's current
41
+ * status — reading history never stops working for a finished mission.
42
+ */
43
+ export declare function getCheckpoint(target: StateReadTarget, id: string): Promise<Checkpoint>;
44
+ //# sourceMappingURL=checkpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../src/coordinatorium/checkpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAA0B,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAExG,OAAO,KAAK,EAAuB,UAAU,EAAE,MAAM,YAAY,CAAC;AA8ClE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAwCrB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAKvG;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAK5F"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Tactical Coordinatorium — Mission Checkpoints (COORDINATORIUM-002).
3
+ *
4
+ * A Checkpoint is a durable, append-only progress note belonging to exactly
5
+ * one Mission. This module owns checkpoint semantics (ID generation,
6
+ * validation, the "only an active mission may be checkpointed" rule) —
7
+ * state-keeper.ts owns persistence and schema shape, exactly mirroring the
8
+ * mission.ts / state-keeper.ts split established in ADR-008.
9
+ *
10
+ * API surface is deliberately append-only: `createCheckpoint`,
11
+ * `listCheckpoints`, `getCheckpoint`. There is no update or delete path —
12
+ * once written, a checkpoint record never changes.
13
+ */
14
+ import { mutateState, readState } from './state-keeper.js';
15
+ import { MissionNotFoundError, MissionNotActiveError, CheckpointNotFoundError, ValidationError } from './errors.js';
16
+ // `context` must not become an unbounded free-form blob (explicit
17
+ // requirement for this story) — every text field carries an explicit upper
18
+ // bound, enforced here at creation time. These are generous for a human/AI
19
+ // progress note but not unbounded.
20
+ const MAX_SUMMARY_LENGTH = 4000;
21
+ const MAX_CONTEXT_LENGTH = 4000;
22
+ const MAX_REMAINING_STEP_LENGTH = 500;
23
+ const MAX_REMAINING_STEPS = 50;
24
+ function escapeRegExp(s) {
25
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
26
+ }
27
+ function requireBoundedString(value, field, maxLength) {
28
+ if (typeof value !== 'string') {
29
+ throw new ValidationError(`"${field}" must be a string`);
30
+ }
31
+ const trimmed = value.trim();
32
+ if (trimmed.length === 0) {
33
+ throw new ValidationError(`"${field}" must be a non-empty string`);
34
+ }
35
+ if (trimmed.length > maxLength) {
36
+ throw new ValidationError(`"${field}" must be at most ${maxLength} characters (got ${trimmed.length})`);
37
+ }
38
+ return trimmed;
39
+ }
40
+ /**
41
+ * Derive the next per-mission checkpoint ordinal by scanning existing
42
+ * checkpoint IDs for the given mission, inside the caller's locked
43
+ * mutateState section — same pattern, same rationale, as mission.ts's
44
+ * nextMissionId: no separate counter field, one source of truth, computed
45
+ * atomically with the write so concurrent creators can never collide.
46
+ */
47
+ function nextCheckpointId(checkpoints, missionId) {
48
+ const re = new RegExp(`^${escapeRegExp(missionId)}-CKPT-(\\d+)$`);
49
+ let max = 0;
50
+ for (const id of Object.keys(checkpoints)) {
51
+ const match = re.exec(id);
52
+ if (match)
53
+ max = Math.max(max, Number(match[1]));
54
+ }
55
+ return `${missionId}-CKPT-${String(max + 1).padStart(4, '0')}`;
56
+ }
57
+ /**
58
+ * Append a checkpoint to an existing, in_progress mission. Validation
59
+ * happens before the lock is taken (pure input checks); the
60
+ * mission-existence check, the active-status check, and the ID derivation
61
+ * all happen inside the same mutateState critical section as the write, so
62
+ * two concurrent calls for the same mission can never collide on an ID or
63
+ * lose an update — a direct consequence of reusing mutateState, not new
64
+ * concurrency design.
65
+ */
66
+ export async function createCheckpoint(target, missionId, input) {
67
+ const summary = requireBoundedString(input.summary, 'summary', MAX_SUMMARY_LENGTH);
68
+ const remainingStepsRaw = input.remainingSteps ?? [];
69
+ if (!Array.isArray(remainingStepsRaw)) {
70
+ throw new ValidationError('"remainingSteps" must be an array of strings');
71
+ }
72
+ if (remainingStepsRaw.length > MAX_REMAINING_STEPS) {
73
+ throw new ValidationError(`"remainingSteps" must contain at most ${MAX_REMAINING_STEPS} items`);
74
+ }
75
+ const remainingSteps = remainingStepsRaw.map((s, i) => requireBoundedString(s, `remainingSteps[${i}]`, MAX_REMAINING_STEP_LENGTH));
76
+ let context = null;
77
+ if (input.context !== undefined && input.context !== null) {
78
+ context = requireBoundedString(input.context, 'context', MAX_CONTEXT_LENGTH);
79
+ }
80
+ const { result } = await mutateState(target, (draft) => {
81
+ const mission = draft.tactical.missions[missionId];
82
+ if (!mission)
83
+ throw new MissionNotFoundError(missionId);
84
+ if (mission.status !== 'in_progress') {
85
+ throw new MissionNotActiveError(missionId, mission.status);
86
+ }
87
+ const id = nextCheckpointId(draft.tactical.checkpoints, missionId);
88
+ const checkpoint = {
89
+ id,
90
+ missionId,
91
+ createdAt: new Date().toISOString(),
92
+ summary,
93
+ remainingSteps,
94
+ context,
95
+ };
96
+ draft.tactical.checkpoints[id] = checkpoint;
97
+ return checkpoint;
98
+ });
99
+ return result;
100
+ }
101
+ /**
102
+ * List a mission's checkpoints, oldest first. Never throws for an unknown
103
+ * or checkpoint-less mission — returns `[]`, consistent with a purely
104
+ * read-side filter over tactical.checkpoints (mirrors listMissions's
105
+ * "no missions yet" behavior). Remains available for a finished mission —
106
+ * only *creation* is gated on mission status.
107
+ */
108
+ export async function listCheckpoints(target, missionId) {
109
+ const state = await readState(target);
110
+ return Object.values(state.tactical.checkpoints)
111
+ .filter((c) => c.missionId === missionId)
112
+ .sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
113
+ }
114
+ /**
115
+ * Fetch a single checkpoint by id, regardless of its mission's current
116
+ * status — reading history never stops working for a finished mission.
117
+ */
118
+ export async function getCheckpoint(target, id) {
119
+ const state = await readState(target);
120
+ const checkpoint = state.tactical.checkpoints[id];
121
+ if (!checkpoint)
122
+ throw new CheckpointNotFoundError(id);
123
+ return checkpoint;
124
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `monolith mission ...` command handlers.
3
+ *
4
+ * Thin layer: parses flags, resolves the instance's state directory, calls
5
+ * into mission.ts, and formats output. All lifecycle/validation logic lives
6
+ * in mission.ts.
7
+ */
8
+ import { SafeWriteError } from '../root/index.js';
9
+ import { LockCreationError } from './errors.js';
10
+ /**
11
+ * A write whose outcome is UNKNOWN (SEC-001B.2) was NOT retried and nothing was
12
+ * cleaned up. The wrapper keeps the machine-readable fields of the original
13
+ * error (`code`, `publication`, `phase`, `path`) and the error itself as
14
+ * `cause`; only the message gains guidance, which depends on WHAT is unknown:
15
+ *
16
+ * - `state`: `state.json` may or may not have been replaced. Look at the
17
+ * missions before repeating a command that is not idempotent (a second
18
+ * `start` would register a second mission).
19
+ * - `lock`: it is the creation of the lock file that is unknown; no state was
20
+ * written. The mission list says nothing about it — inspect the lock and its
21
+ * staging files, and remove nothing while another command may be running.
22
+ */
23
+ export declare class UnknownWriteOutcomeError extends Error {
24
+ readonly code: string;
25
+ readonly publication: "UNKNOWN";
26
+ readonly phase: string;
27
+ readonly path: string;
28
+ readonly subject: 'state' | 'lock';
29
+ readonly cause: unknown;
30
+ constructor(original: SafeWriteError | LockCreationError);
31
+ }
32
+ export declare function withWriteGuidance<T>(run: () => Promise<T>): Promise<T>;
33
+ export declare function runMissionStart(args: string[], targetDir?: string): Promise<void>;
34
+ export declare function runMissionList(_args: string[], targetDir?: string): Promise<void>;
35
+ export declare function runMissionShow(args: string[], targetDir?: string): Promise<void>;
36
+ export declare function runMissionCheckpoint(args: string[], targetDir?: string): Promise<void>;
37
+ export declare function runMissionCheckpoints(args: string[], targetDir?: string): Promise<void>;
38
+ export declare function runMissionFinish(args: string[], targetDir?: string): Promise<void>;
39
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/coordinatorium/cli.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AAEH,OAAO,EAAgE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAChH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAwBhD;;;;;;;;;;;;GAYG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAG,SAAS,CAAU;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,QAAQ,EAAE,cAAc,GAAG,iBAAiB;CAiBzD;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAS5E;AA8BD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAS9F;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB9F;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAO7F;AAeD,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBnG;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAepG;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAe/F"}
@@ -0,0 +1,203 @@
1
+ /* eslint-disable no-console */
2
+ /**
3
+ * `monolith mission ...` command handlers.
4
+ *
5
+ * Thin layer: parses flags, resolves the instance's state directory, calls
6
+ * into mission.ts, and formats output. All lifecycle/validation logic lives
7
+ * in mission.ts.
8
+ */
9
+ import { resolveMonolithRoot, assertContainedFile, reportWriteWarning, SafeWriteError } from '../root/index.js';
10
+ import { LockCreationError } from './errors.js';
11
+ import { STATE_FILE_NAME, projectTarget } from './state-keeper.js';
12
+ import { startMission, listMissions, getMission, finishMission } from './mission.js';
13
+ import { createCheckpoint, listCheckpoints } from './checkpoint.js';
14
+ // Reads use the same PROJECT scope as writes (SEC-001B.1-R): the project root is
15
+ // the explicit boundary, so a symlink anywhere below it is refused.
16
+ function readTargetFor(targetDir) {
17
+ const root = resolveMonolithRoot(targetDir);
18
+ assertContainedFile(root, STATE_FILE_NAME);
19
+ return projectTarget(root.projectRoot, root.rootAbs);
20
+ }
21
+ // Writes always use PROJECT scope (ADR-012): the project root is the boundary,
22
+ // so a symlinked `monolithRoot` or intermediate directory is refused even when
23
+ // it stays inside the project.
24
+ function writeTargetFor(targetDir) {
25
+ const root = resolveMonolithRoot(targetDir);
26
+ assertContainedFile(root, STATE_FILE_NAME);
27
+ assertContainedFile(root, `${STATE_FILE_NAME}.lock`);
28
+ return projectTarget(root.projectRoot, root.rootAbs, (w) => reportWriteWarning(w));
29
+ }
30
+ /**
31
+ * A write whose outcome is UNKNOWN (SEC-001B.2) was NOT retried and nothing was
32
+ * cleaned up. The wrapper keeps the machine-readable fields of the original
33
+ * error (`code`, `publication`, `phase`, `path`) and the error itself as
34
+ * `cause`; only the message gains guidance, which depends on WHAT is unknown:
35
+ *
36
+ * - `state`: `state.json` may or may not have been replaced. Look at the
37
+ * missions before repeating a command that is not idempotent (a second
38
+ * `start` would register a second mission).
39
+ * - `lock`: it is the creation of the lock file that is unknown; no state was
40
+ * written. The mission list says nothing about it — inspect the lock and its
41
+ * staging files, and remove nothing while another command may be running.
42
+ */
43
+ export class UnknownWriteOutcomeError extends Error {
44
+ code;
45
+ publication = 'UNKNOWN';
46
+ phase;
47
+ path;
48
+ subject;
49
+ cause;
50
+ constructor(original) {
51
+ const lock = original instanceof LockCreationError;
52
+ const guidance = lock
53
+ ? `The outcome of creating the lock is UNKNOWN. It was not retried and nothing was removed; no state was written. ` +
54
+ `Inspect, read-only, "${original.lockPath}" and any "${original.lockPath}.creating.*" or "${original.lockPath}.recovery" ` +
55
+ 'next to it. MONOLITH never deletes these automatically (FS-CLEAN-001); remove nothing while another monolith ' +
56
+ 'command may be running.'
57
+ : 'The outcome of this write is UNKNOWN. It was not retried. Check the current state with ' +
58
+ '`monolith mission list` (and `monolith mission show <id>`) before running this command again.';
59
+ super(`${original.message}\n${guidance}`);
60
+ this.name = 'UnknownWriteOutcomeError';
61
+ this.code = original.code;
62
+ this.phase = original.phase;
63
+ this.path = lock ? original.lockPath : original.path;
64
+ this.subject = lock ? 'lock' : 'state';
65
+ this.cause = original;
66
+ }
67
+ }
68
+ export async function withWriteGuidance(run) {
69
+ try {
70
+ return await run();
71
+ }
72
+ catch (err) {
73
+ if ((err instanceof SafeWriteError || err instanceof LockCreationError) && err.publication === 'UNKNOWN') {
74
+ throw new UnknownWriteOutcomeError(err);
75
+ }
76
+ throw err;
77
+ }
78
+ }
79
+ function parseFlag(args, name) {
80
+ const i = args.indexOf(name);
81
+ if (i === -1 || i === args.length - 1)
82
+ return undefined;
83
+ return args[i + 1];
84
+ }
85
+ /** Like parseFlag, but collects every occurrence (e.g. repeated `--remaining`). */
86
+ function parseRepeatedFlag(args, name) {
87
+ const values = [];
88
+ for (let i = 0; i < args.length - 1; i++) {
89
+ if (args[i] === name)
90
+ values.push(args[i + 1]);
91
+ }
92
+ return values;
93
+ }
94
+ function formatMission(m) {
95
+ const lines = [
96
+ ` id: ${m.id}`,
97
+ ` title: ${m.title}`,
98
+ ` system: ${m.system}`,
99
+ ` status: ${m.status}`,
100
+ ` startedAt: ${m.startedAt}`,
101
+ ` updatedAt: ${m.updatedAt}`,
102
+ ` summary: ${m.summary ?? '(none)'}`,
103
+ ];
104
+ return lines.join('\n');
105
+ }
106
+ export async function runMissionStart(args, targetDir = process.cwd()) {
107
+ const title = parseFlag(args, '--title');
108
+ const system = parseFlag(args, '--system');
109
+ if (!title || !system) {
110
+ throw new Error('Usage: monolith mission start --title "..." --system "..."');
111
+ }
112
+ const mission = await withWriteGuidance(() => startMission(writeTargetFor(targetDir), { title, system }));
113
+ console.log(`Started mission ${mission.id} (${mission.status})`);
114
+ console.log(formatMission(mission));
115
+ }
116
+ export async function runMissionList(_args, targetDir = process.cwd()) {
117
+ const missions = await listMissions(readTargetFor(targetDir));
118
+ if (missions.length === 0) {
119
+ console.log('No missions.');
120
+ return;
121
+ }
122
+ const active = missions.filter((m) => m.status === 'in_progress');
123
+ const finished = missions.filter((m) => m.status !== 'in_progress');
124
+ console.log(`Active missions (${active.length}):`);
125
+ if (active.length === 0)
126
+ console.log(' (none)');
127
+ for (const m of active) {
128
+ console.log(` ${m.id} [${m.status}] ${m.title} (${m.system})`);
129
+ }
130
+ console.log('');
131
+ console.log(`Finished missions (${finished.length}):`);
132
+ if (finished.length === 0)
133
+ console.log(' (none)');
134
+ for (const m of finished) {
135
+ console.log(` ${m.id} [${m.status}] ${m.title} (${m.system})`);
136
+ }
137
+ }
138
+ export async function runMissionShow(args, targetDir = process.cwd()) {
139
+ const id = args[0];
140
+ if (!id) {
141
+ throw new Error('Usage: monolith mission show <id>');
142
+ }
143
+ const mission = await getMission(readTargetFor(targetDir), id);
144
+ console.log(formatMission(mission));
145
+ }
146
+ function formatCheckpoint(c) {
147
+ const lines = [
148
+ ` id: ${c.id}`,
149
+ ` missionId: ${c.missionId}`,
150
+ ` createdAt: ${c.createdAt}`,
151
+ ` summary: ${c.summary}`,
152
+ ` remainingSteps: ${c.remainingSteps.length === 0 ? '(none)' : ''}`,
153
+ ...c.remainingSteps.map((s) => ` - ${s}`),
154
+ ` context: ${c.context ?? '(none)'}`,
155
+ ];
156
+ return lines.join('\n');
157
+ }
158
+ export async function runMissionCheckpoint(args, targetDir = process.cwd()) {
159
+ const missionId = args[0];
160
+ const summary = parseFlag(args, '--summary');
161
+ if (!missionId || !summary) {
162
+ throw new Error('Usage: monolith mission checkpoint <missionId> --summary "..." [--remaining "..." ...] [--context "..."]');
163
+ }
164
+ const remainingSteps = parseRepeatedFlag(args, '--remaining');
165
+ const context = parseFlag(args, '--context');
166
+ const checkpoint = await withWriteGuidance(() => createCheckpoint(writeTargetFor(targetDir), missionId, {
167
+ summary,
168
+ remainingSteps,
169
+ context: context ?? null,
170
+ }));
171
+ console.log(`Created checkpoint ${checkpoint.id} for mission ${checkpoint.missionId}`);
172
+ console.log(formatCheckpoint(checkpoint));
173
+ }
174
+ export async function runMissionCheckpoints(args, targetDir = process.cwd()) {
175
+ const missionId = args[0];
176
+ if (!missionId) {
177
+ throw new Error('Usage: monolith mission checkpoints <missionId>');
178
+ }
179
+ const checkpoints = await listCheckpoints(readTargetFor(targetDir), missionId);
180
+ if (checkpoints.length === 0) {
181
+ console.log(`No checkpoints for mission ${missionId}.`);
182
+ return;
183
+ }
184
+ console.log(`Checkpoints for mission ${missionId} (${checkpoints.length}):`);
185
+ for (const c of checkpoints) {
186
+ console.log('');
187
+ console.log(formatCheckpoint(c));
188
+ }
189
+ }
190
+ export async function runMissionFinish(args, targetDir = process.cwd()) {
191
+ const id = args[0];
192
+ const result = parseFlag(args, '--result');
193
+ const summary = parseFlag(args, '--summary');
194
+ if (!id || !result) {
195
+ throw new Error('Usage: monolith mission finish <id> --result completed|failed|cancelled [--summary "..."]');
196
+ }
197
+ const mission = await withWriteGuidance(() => finishMission(writeTargetFor(targetDir), id, {
198
+ result: result,
199
+ summary: summary ?? undefined,
200
+ }));
201
+ console.log(`Mission ${mission.id} finished as ${mission.status}`);
202
+ console.log(formatMission(mission));
203
+ }